1 /*************************************************************************
3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
5 * Copyright 2008 by Sun Microsystems, Inc.
7 * OpenOffice.org - a multi-platform office productivity suite
9 * $RCSfile: textdoc.cxx,v $
12 * This file is part of OpenOffice.org.
14 * OpenOffice.org is free software: you can redistribute it and/or modify
15 * it under the terms of the GNU Lesser General Public License version 3
16 * only, as published by the Free Software Foundation.
18 * OpenOffice.org is distributed in the hope that it will be useful,
19 * but WITHOUT ANY WARRANTY; without even the implied warranty of
20 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
21 * GNU Lesser General Public License version 3 for more details
22 * (a copy is included in the LICENSE file that accompanied this code).
24 * You should have received a copy of the GNU Lesser General Public License
25 * version 3 along with OpenOffice.org. If not, see
26 * <http://www.openoffice.org/license.html>
27 * for a copy of the LGPLv3 License.
29 ************************************************************************/
31 // MARKER(update_precomp.py): autogen include statement, do not remove
32 #include "precompiled_svtools.hxx"
34 #include <textdoc.hxx>
38 SV_IMPL_PTRARR( TextCharAttribs
, TextCharAttribPtr
);
42 // Vergleichmethode wird von QuickSort gerufen...
45 #if defined( PM2 ) && (!defined( CSET ) && !defined ( MTW ) && !defined( WTC ))
59 CompareStart( const void* pFirst
, const void* pSecond
)
61 if ( (*((TextCharAttrib
**)pFirst
))->GetStart() < (*((TextCharAttrib
**)pSecond
))->GetStart() )
63 else if ( (*((TextCharAttrib
**)pFirst
))->GetStart() > (*((TextCharAttrib
**)pSecond
))->GetStart() )
69 \f// -------------------------------------------------------------------------
70 // (+) class TextCharAttrib
71 // -------------------------------------------------------------------------
72 TextCharAttrib::TextCharAttrib( const TextAttrib
& rAttr
, USHORT nStart
, USHORT nEnd
)
74 mpAttr
= rAttr
.Clone();
79 TextCharAttrib::TextCharAttrib( const TextCharAttrib
& rTextCharAttrib
)
81 mpAttr
= rTextCharAttrib
.GetAttr().Clone();
82 mnStart
= rTextCharAttrib
.mnStart
;
83 mnEnd
= rTextCharAttrib
.mnEnd
;
86 TextCharAttrib::~TextCharAttrib()
91 \f// -------------------------------------------------------------------------
92 // (+) class TextCharAttribList
93 // -------------------------------------------------------------------------
95 TextCharAttribList::TextCharAttribList()
97 mbHasEmptyAttribs
= FALSE
;
100 TextCharAttribList::~TextCharAttribList()
105 void TextCharAttribList::Clear( BOOL bDestroyAttribs
)
107 if ( bDestroyAttribs
)
108 TextCharAttribs::DeleteAndDestroy( 0, Count() );
110 TextCharAttribs::Remove( 0, Count() );
114 void TextCharAttribList::InsertAttrib( TextCharAttrib
* pAttrib
)
116 if ( pAttrib
->IsEmpty() )
117 mbHasEmptyAttribs
= TRUE
;
119 const USHORT nCount
= Count();
120 const USHORT nStart
= pAttrib
->GetStart(); // vielleicht besser fuer Comp.Opt.
121 BOOL bInserted
= FALSE
;
122 for ( USHORT x
= 0; x
< nCount
; x
++ )
124 TextCharAttrib
* pCurAttrib
= GetObject( x
);
125 if ( pCurAttrib
->GetStart() > nStart
)
127 Insert( pAttrib
, x
);
133 Insert( pAttrib
, nCount
);
136 void TextCharAttribList::ResortAttribs()
139 qsort( (void*)GetData(), Count(), sizeof( TextCharAttrib
* ), CompareStart
);
142 TextCharAttrib
* TextCharAttribList::FindAttrib( USHORT nWhich
, USHORT nPos
)
144 // Rueckwaerts, falls eins dort endet, das naechste startet.
145 // => Das startende gilt...
147 for ( USHORT nAttr
= Count(); nAttr
; )
149 TextCharAttrib
* pAttr
= GetObject( --nAttr
);
151 if ( pAttr
->GetEnd() < nPos
)
154 if ( ( pAttr
->Which() == nWhich
) && pAttr
->IsIn(nPos
) )
160 TextCharAttrib
* TextCharAttribList::FindNextAttrib( USHORT nWhich
, USHORT nFromPos
, USHORT nMaxPos
) const
162 DBG_ASSERT( nWhich
, "FindNextAttrib: Which?" );
163 const USHORT nAttribs
= Count();
164 for ( USHORT nAttr
= 0; nAttr
< nAttribs
; nAttr
++ )
166 TextCharAttrib
* pAttr
= GetObject( nAttr
);
167 if ( ( pAttr
->GetStart() >= nFromPos
) &&
168 ( pAttr
->GetEnd() <= nMaxPos
) &&
169 ( pAttr
->Which() == nWhich
) )
175 BOOL
TextCharAttribList::HasAttrib( USHORT nWhich
) const
177 for ( USHORT nAttr
= Count(); nAttr
; )
179 const TextCharAttrib
* pAttr
= GetObject( --nAttr
);
180 if ( pAttr
->Which() == nWhich
)
186 BOOL
TextCharAttribList::HasBoundingAttrib( USHORT nBound
)
188 // Rueckwaerts, falls eins dort endet, das naechste startet.
189 // => Das startende gilt...
190 for ( USHORT nAttr
= Count(); nAttr
; )
192 TextCharAttrib
* pAttr
= GetObject( --nAttr
);
194 if ( pAttr
->GetEnd() < nBound
)
197 if ( ( pAttr
->GetStart() == nBound
) || ( pAttr
->GetEnd() == nBound
) )
203 TextCharAttrib
* TextCharAttribList::FindEmptyAttrib( USHORT nWhich
, USHORT nPos
)
205 if ( !mbHasEmptyAttribs
)
208 const USHORT nAttribs
= Count();
209 for ( USHORT nAttr
= 0; nAttr
< nAttribs
; nAttr
++ )
211 TextCharAttrib
* pAttr
= GetObject( nAttr
);
212 if ( pAttr
->GetStart() > nPos
)
215 if ( ( pAttr
->GetStart() == nPos
) && ( pAttr
->GetEnd() == nPos
) && ( pAttr
->Which() == nWhich
) )
221 void TextCharAttribList::DeleteEmptyAttribs()
223 for ( USHORT nAttr
= 0; nAttr
< Count(); nAttr
++ )
225 TextCharAttrib
* pAttr
= GetObject( nAttr
);
226 if ( pAttr
->IsEmpty() )
233 mbHasEmptyAttribs
= FALSE
;
237 BOOL
TextCharAttribList::DbgCheckAttribs()
240 for ( USHORT nAttr
= 0; nAttr
< Count(); nAttr
++ )
242 TextCharAttrib
* pAttr
= GetObject( nAttr
);
243 if ( pAttr
->GetStart() > pAttr
->GetEnd() )
246 DBG_ERROR( "Attr verdreht" );
253 \f// -------------------------------------------------------------------------
254 // (+) class TextNode
255 // -------------------------------------------------------------------------
257 TextNode::TextNode( const String
& rText
) :
262 void TextNode::ExpandAttribs( USHORT nIndex
, USHORT nNew
)
267 BOOL bResort
= FALSE
;
268 USHORT nAttribs
= maCharAttribs
.Count();
269 for ( USHORT nAttr
= 0; nAttr
< nAttribs
; nAttr
++ )
271 TextCharAttrib
* pAttrib
= maCharAttribs
.GetAttrib( nAttr
);
272 if ( pAttrib
->GetEnd() >= nIndex
)
274 // Alle Attribute hinter der Einfuegeposition verschieben...
275 if ( pAttrib
->GetStart() > nIndex
)
277 pAttrib
->MoveForward( nNew
);
279 // 0: Leeres Attribut expandieren, wenn an Einfuegestelle
280 else if ( pAttrib
->IsEmpty() )
282 // Index nicht pruefen, leeres durfte nur dort liegen.
283 // Wenn spaeter doch Ueberpruefung:
284 // Spezialfall: Start == 0; AbsLen == 1, nNew = 1 => Expand, weil durch Absatzumbruch!
285 // Start <= nIndex, End >= nIndex => Start=End=nIndex!
286 // if ( pAttrib->GetStart() == nIndex )
287 pAttrib
->Expand( nNew
);
289 // 1: Attribut startet davor, geht bis Index...
290 else if ( pAttrib
->GetEnd() == nIndex
) // Start muss davor liegen
292 // Nur expandieren, wenn kein Feature,
293 // und wenn nicht in ExcludeListe!
294 // Sonst geht z.B. ein UL bis zum neuen ULDB, beide expandieren
295 if ( !maCharAttribs
.FindEmptyAttrib( pAttrib
->Which(), nIndex
) )
297 pAttrib
->Expand( nNew
);
302 // 2: Attribut startet davor, geht hinter Index...
303 else if ( ( pAttrib
->GetStart() < nIndex
) && ( pAttrib
->GetEnd() > nIndex
) )
305 pAttrib
->Expand( nNew
);
307 // 3: Attribut startet auf Index...
308 else if ( pAttrib
->GetStart() == nIndex
)
312 pAttrib
->Expand( nNew
);
313 // bResort = TRUE; // es gibt ja keine Features mehr...
316 pAttrib
->MoveForward( nNew
);
320 DBG_ASSERT( pAttrib
->GetStart() <= pAttrib
->GetEnd(), "Expand: Attribut verdreht!" );
321 DBG_ASSERT( ( pAttrib
->GetEnd() <= maText
.Len() ), "Expand: Attrib groesser als Absatz!" );
322 DBG_ASSERT( !pAttrib
->IsEmpty(), "Leeres Attribut nach ExpandAttribs?" );
326 maCharAttribs
.ResortAttribs();
329 DBG_ASSERT( CheckOrderedList( (TextCharAttribs
*)&maCharAttribs
), "Expand: Start-Liste verdreht" );
333 void TextNode::CollapsAttribs( USHORT nIndex
, USHORT nDeleted
)
338 BOOL bResort
= FALSE
;
339 USHORT nEndChanges
= nIndex
+nDeleted
;
341 for ( USHORT nAttr
= 0; nAttr
< maCharAttribs
.Count(); nAttr
++ )
343 TextCharAttrib
* pAttrib
= maCharAttribs
.GetAttrib( nAttr
);
344 BOOL bDelAttr
= FALSE
;
345 if ( pAttrib
->GetEnd() >= nIndex
)
347 // Alles Attribute hinter der Einfuegeposition verschieben...
348 if ( pAttrib
->GetStart() >= nEndChanges
)
350 pAttrib
->MoveBackward( nDeleted
);
352 // 1. Innenliegende Attribute loeschen...
353 else if ( ( pAttrib
->GetStart() >= nIndex
) && ( pAttrib
->GetEnd() <= nEndChanges
) )
355 // Spezialfall: Attrubt deckt genau den Bereich ab
356 // => als leeres Attribut behalten.
357 if ( ( pAttrib
->GetStart() == nIndex
) && ( pAttrib
->GetEnd() == nEndChanges
) )
358 pAttrib
->GetEnd() = nIndex
; // leer
362 // 2. Attribut beginnt davor, endet drinnen oder dahinter...
363 else if ( ( pAttrib
->GetStart() <= nIndex
) && ( pAttrib
->GetEnd() > nIndex
) )
365 if ( pAttrib
->GetEnd() <= nEndChanges
) // endet drinnen
366 pAttrib
->GetEnd() = nIndex
;
368 pAttrib
->Collaps( nDeleted
); // endet dahinter
370 // 3. Attribut beginnt drinnen, endet dahinter...
371 else if ( ( pAttrib
->GetStart() >= nIndex
) && ( pAttrib
->GetEnd() > nEndChanges
) )
373 // Features duerfen nicht expandieren!
374 pAttrib
->GetStart() = nEndChanges
;
375 pAttrib
->MoveBackward( nDeleted
);
379 DBG_ASSERT( pAttrib
->GetStart() <= pAttrib
->GetEnd(), "Collaps: Attribut verdreht!" );
380 DBG_ASSERT( ( pAttrib
->GetEnd() <= maText
.Len()) || bDelAttr
, "Collaps: Attrib groesser als Absatz!" );
381 if ( bDelAttr
/* || pAttrib->IsEmpty() */ )
384 maCharAttribs
.RemoveAttrib( nAttr
);
388 else if ( pAttrib
->IsEmpty() )
389 maCharAttribs
.HasEmptyAttribs() = TRUE
;
393 maCharAttribs
.ResortAttribs();
396 DBG_ASSERT( CheckOrderedList( (TextCharAttribs
)&maCharAttribs
), "Collaps: Start-Liste verdreht" );
400 void TextNode::InsertText( USHORT nPos
, const String
& rText
)
402 maText
.Insert( rText
, nPos
);
403 ExpandAttribs( nPos
, rText
.Len() );
406 void TextNode::InsertText( USHORT nPos
, sal_Unicode c
)
408 maText
.Insert( c
, nPos
);
409 ExpandAttribs( nPos
, 1 );
412 void TextNode::RemoveText( USHORT nPos
, USHORT nChars
)
414 maText
.Erase( nPos
, nChars
);
415 CollapsAttribs( nPos
, nChars
);
418 TextNode
* TextNode::Split( USHORT nPos
, BOOL bKeepEndingAttribs
)
421 if ( nPos
< maText
.Len() )
423 aNewText
= maText
.Copy( nPos
);
424 maText
.Erase( nPos
);
426 TextNode
* pNew
= new TextNode( aNewText
);
428 for ( USHORT nAttr
= 0; nAttr
< maCharAttribs
.Count(); nAttr
++ )
430 TextCharAttrib
* pAttrib
= maCharAttribs
.GetAttrib( nAttr
);
431 if ( pAttrib
->GetEnd() < nPos
)
433 // bleiben unveraendert....
436 else if ( pAttrib
->GetEnd() == nPos
)
438 // muessen als leeres Attribut kopiert werden.
439 // !FindAttrib nur sinnvoll, wenn Rueckwaerts durch Liste!
440 if ( bKeepEndingAttribs
&& !pNew
->maCharAttribs
.FindAttrib( pAttrib
->Which(), 0 ) )
442 TextCharAttrib
* pNewAttrib
= new TextCharAttrib( *pAttrib
);
443 pNewAttrib
->GetStart() = 0;
444 pNewAttrib
->GetEnd() = 0;
445 pNew
->maCharAttribs
.InsertAttrib( pNewAttrib
);
448 else if ( pAttrib
->IsInside( nPos
) || ( !nPos
&& !pAttrib
->GetStart() ) )
450 // Wenn ganz vorne gecuttet wird, muss das Attribut erhalten bleiben!
451 // muessen kopiert und geaendert werden
452 TextCharAttrib
* pNewAttrib
= new TextCharAttrib( *pAttrib
);
453 pNewAttrib
->GetStart() = 0;
454 pNewAttrib
->GetEnd() = pAttrib
->GetEnd()-nPos
;
455 pNew
->maCharAttribs
.InsertAttrib( pNewAttrib
);
457 pAttrib
->GetEnd() = nPos
;
461 DBG_ASSERT( pAttrib
->GetStart() >= nPos
, "Start < nPos!" );
462 DBG_ASSERT( pAttrib
->GetEnd() >= nPos
, "End < nPos!" );
463 // alle dahinter verschieben in den neuen Node (this)
464 maCharAttribs
.RemoveAttrib( nAttr
);
465 pNew
->maCharAttribs
.InsertAttrib( pAttrib
);
466 pAttrib
->GetStart() = pAttrib
->GetStart() - nPos
;
467 pAttrib
->GetEnd() = pAttrib
->GetEnd() - nPos
;
474 void TextNode::Append( const TextNode
& rNode
)
476 USHORT nOldLen
= maText
.Len();
478 maText
+= rNode
.GetText();
481 DBG_ASSERT( maCharAttribs
.DbgCheckAttribs(), "Attribute VOR AppendAttribs kaputt" );
484 const USHORT nAttribs
= rNode
.GetCharAttribs().Count();
485 for ( USHORT nAttr
= 0; nAttr
< nAttribs
; nAttr
++ )
487 TextCharAttrib
* pAttrib
= rNode
.GetCharAttribs().GetAttrib( nAttr
);
488 BOOL bMelted
= FALSE
;
489 if ( pAttrib
->GetStart() == 0 )
491 // Evtl koennen Attribute zusammengefasst werden:
492 USHORT nTmpAttribs
= maCharAttribs
.Count();
493 for ( USHORT nTmpAttr
= 0; nTmpAttr
< nTmpAttribs
; nTmpAttr
++ )
495 TextCharAttrib
* pTmpAttrib
= maCharAttribs
.GetAttrib( nTmpAttr
);
497 if ( pTmpAttrib
->GetEnd() == nOldLen
)
499 if ( ( pTmpAttrib
->Which() == pAttrib
->Which() ) &&
500 ( pTmpAttrib
->GetAttr() == pAttrib
->GetAttr() ) )
502 pTmpAttrib
->GetEnd() =
503 pTmpAttrib
->GetEnd() + pAttrib
->GetLen();
505 break; // es kann nur eins von der Sorte an der Stelle geben
513 TextCharAttrib
* pNewAttrib
= new TextCharAttrib( *pAttrib
);
514 pNewAttrib
->GetStart() = pNewAttrib
->GetStart() + nOldLen
;
515 pNewAttrib
->GetEnd() = pNewAttrib
->GetEnd() + nOldLen
;
516 maCharAttribs
.InsertAttrib( pNewAttrib
);
521 DBG_ASSERT( maCharAttribs
.DbgCheckAttribs(), "Attribute NACH AppendAttribs kaputt" );
525 \f// -------------------------------------------------------------------------
527 // -------------------------------------------------------------------------
539 void TextDoc::Clear()
544 void TextDoc::DestroyTextNodes()
546 for ( ULONG nNode
= 0; nNode
< maTextNodes
.Count(); nNode
++ )
547 delete maTextNodes
.GetObject( nNode
);
551 String
TextDoc::GetText( const sal_Unicode
* pSep
) const
553 ULONG nLen
= GetTextLen( pSep
);
554 ULONG nNodes
= maTextNodes
.Count();
556 if ( nLen
> STRING_MAXLEN
)
558 DBG_ERROR( "Text zu gross fuer String" );
563 ULONG nLastNode
= nNodes
-1;
564 for ( ULONG nNode
= 0; nNode
< nNodes
; nNode
++ )
566 TextNode
* pNode
= maTextNodes
.GetObject( nNode
);
567 String
aTmp( pNode
->GetText() );
569 if ( pSep
&& ( nNode
!= nLastNode
) )
576 XubString
TextDoc::GetText( ULONG nPara
) const
579 TextNode
* pNode
= ( nPara
< maTextNodes
.Count() ) ? maTextNodes
.GetObject( nPara
) : 0;
581 aText
= pNode
->GetText();
587 ULONG
TextDoc::GetTextLen( const xub_Unicode
* pSep
, const TextSelection
* pSel
) const
590 ULONG nNodes
= maTextNodes
.Count();
593 ULONG nStartNode
= 0;
594 ULONG nEndNode
= nNodes
-1;
597 nStartNode
= pSel
->GetStart().GetPara();
598 nEndNode
= pSel
->GetEnd().GetPara();
601 for ( ULONG nNode
= nStartNode
; nNode
<= nEndNode
; nNode
++ )
603 TextNode
* pNode
= maTextNodes
.GetObject( nNode
);
606 ULONG nE
= pNode
->GetText().Len();
607 if ( pSel
&& ( nNode
== pSel
->GetStart().GetPara() ) )
608 nS
= pSel
->GetStart().GetIndex();
609 if ( pSel
&& ( nNode
== pSel
->GetEnd().GetPara() ) )
610 nE
= pSel
->GetEnd().GetIndex();
616 nLen
+= (nEndNode
-nStartNode
) * String( pSep
).Len();
622 TextPaM
TextDoc::InsertText( const TextPaM
& rPaM
, xub_Unicode c
)
624 DBG_ASSERT( c
!= 0x0A, "TextDoc::InsertText: Zeilentrenner in Absatz nicht erlaubt!" );
625 DBG_ASSERT( c
!= 0x0D, "TextDoc::InsertText: Zeilentrenner in Absatz nicht erlaubt!" );
627 TextNode
* pNode
= maTextNodes
.GetObject( rPaM
.GetPara() );
628 pNode
->InsertText( rPaM
.GetIndex(), c
);
630 TextPaM
aPaM( rPaM
.GetPara(), rPaM
.GetIndex()+1 );
634 TextPaM
TextDoc::InsertText( const TextPaM
& rPaM
, const XubString
& rStr
)
636 DBG_ASSERT( rStr
.Search( 0x0A ) == STRING_NOTFOUND
, "TextDoc::InsertText: Zeilentrenner in Absatz nicht erlaubt!" );
637 DBG_ASSERT( rStr
.Search( 0x0D ) == STRING_NOTFOUND
, "TextDoc::InsertText: Zeilentrenner in Absatz nicht erlaubt!" );
639 TextNode
* pNode
= maTextNodes
.GetObject( rPaM
.GetPara() );
640 pNode
->InsertText( rPaM
.GetIndex(), rStr
);
642 TextPaM
aPaM( rPaM
.GetPara(), rPaM
.GetIndex()+rStr
.Len() );
646 TextPaM
TextDoc::InsertParaBreak( const TextPaM
& rPaM
, BOOL bKeepEndingAttribs
)
648 TextNode
* pNode
= maTextNodes
.GetObject( rPaM
.GetPara() );
649 TextNode
* pNew
= pNode
->Split( rPaM
.GetIndex(), bKeepEndingAttribs
);
651 maTextNodes
.Insert( pNew
, rPaM
.GetPara()+1 );
653 TextPaM
aPaM( rPaM
.GetPara()+1, 0 );
657 TextPaM
TextDoc::ConnectParagraphs( TextNode
* pLeft
, TextNode
* pRight
)
659 USHORT nPrevLen
= pLeft
->GetText().Len();
660 pLeft
->Append( *pRight
);
662 // der rechte verschwindet.
663 ULONG nRight
= maTextNodes
.GetPos( pRight
);
664 maTextNodes
.Remove( nRight
);
667 ULONG nLeft
= maTextNodes
.GetPos( pLeft
);
668 TextPaM
aPaM( nLeft
, nPrevLen
);
672 TextPaM
TextDoc::RemoveChars( const TextPaM
& rPaM
, USHORT nChars
)
674 TextNode
* pNode
= maTextNodes
.GetObject( rPaM
.GetPara() );
675 pNode
->RemoveText( rPaM
.GetIndex(), nChars
);
680 BOOL
TextDoc::IsValidPaM( const TextPaM
& rPaM
)
682 if ( rPaM
.GetPara() >= maTextNodes
.Count() )
684 DBG_ERROR( "PaM: Para out of range" );
687 TextNode
* pNode
= maTextNodes
.GetObject( rPaM
.GetPara() );
688 if ( rPaM
.GetIndex() > pNode
->GetText().Len() )
690 DBG_ERROR( "PaM: Index out of range" );
698 void TextDoc::InsertAttribInSelection( TextNode* pNode, USHORT nStart, USHORT nEnd, const SfxPoolItem& rPoolItem )
700 DBG_ASSERT( pNode, "Wohin mit dem Attribut?" );
701 DBG_ASSERT( nEnd <= pNode->Len(), "InsertAttrib: Attribut zu gross!" );
704 // dieses endet am Anfang der Selektion => kann erweitert werden
705 TextCharAttrib* pEndingAttrib = 0;
706 // dieses startet am Ende der Selektion => kann erweitert werden
707 TextCharAttrib* pStartingAttrib = 0;
709 DBG_ASSERT( nStart <= nEnd, "Kleiner Rechenfehler in InsertAttribInSelection" );
711 RemoveAttribs( pNode, nStart, nEnd, pStartingAttrib, pEndingAttrib, rPoolItem.Which() );
713 if ( pStartingAttrib && pEndingAttrib &&
714 ( *(pStartingAttrib->GetItem()) == rPoolItem ) &&
715 ( *(pEndingAttrib->GetItem()) == rPoolItem ) )
717 // wird ein groesses Attribut.
718 pEndingAttrib->GetEnd() = pStartingAttrib->GetEnd();
719 pCurPool->Remove( *(pStartingAttrib->GetItem()) );
720 pNode->GetCharAttribs().GetAttribs().Remove( pNode->GetCharAttribs().GetAttribs().GetPos( pStartingAttrib ) );
721 delete pStartingAttrib;
723 else if ( pStartingAttrib && ( *(pStartingAttrib->GetItem()) == rPoolItem ) )
724 pStartingAttrib->GetStart() = nStart;
725 else if ( pEndingAttrib && ( *(pEndingAttrib->GetItem()) == rPoolItem ) )
726 pEndingAttrib->GetEnd() = nEnd;
728 InsertAttrib( rPoolItem, pNode, nStart, nEnd );
730 if ( pStartingAttrib )
731 pNode->GetCharAttribs().ResortAttribs();
734 BOOL TextDoc::RemoveAttribs( TextNode* pNode, USHORT nStart, USHORT nEnd, USHORT nWhich )
736 TextCharAttrib* pStarting;
737 TextCharAttrib* pEnding;
738 return RemoveAttribs( pNode, nStart, nEnd, pStarting, pEnding, nWhich );
741 BOOL TextDoc::RemoveAttribs( TextNode* pNode, USHORT nStart, USHORT nEnd, TextCharAttrib*& rpStarting, TextCharAttrib*& rpEnding, USHORT nWhich )
743 DBG_ASSERT( pNode, "Wohin mit dem Attribut?" );
744 DBG_ASSERT( nEnd <= pNode->Len(), "InsertAttrib: Attribut zu gross!" );
746 // dieses endet am Anfang der Selektion => kann erweitert werden
748 // dieses startet am Ende der Selektion => kann erweitert werden
751 BOOL bChanged = FALSE;
753 DBG_ASSERT( nStart <= nEnd, "Kleiner Rechenfehler in InsertAttribInSelection" );
755 // ueber die Attribute iterieren...
757 TextCharAttrib* pAttr = GetAttrib( pNode->GetCharAttribs().GetAttribs(), nAttr );
760 BOOL bRemoveAttrib = FALSE;
761 if ( !nWhich || ( pAttr->Which() == nWhich ) )
763 // Attribut beginnt in Selection
764 if ( ( pAttr->GetStart() >= nStart ) && ( pAttr->GetStart() <= nEnd ) )
767 if ( pAttr->GetEnd() > nEnd )
769 pAttr->GetStart() = nEnd; // dann faengt es dahinter an
771 break; // es kann kein weiteres Attrib hier liegen
773 else if ( !pAttr->IsFeature() || ( pAttr->GetStart() == nStart ) )
775 // Feature nur loeschen, wenn genau an der Stelle
776 bRemoveAttrib = TRUE;
780 // Attribut endet in Selection
781 else if ( ( pAttr->GetEnd() >= nStart ) && ( pAttr->GetEnd() <= nEnd ) )
784 if ( ( pAttr->GetStart() < nStart ) && !pAttr->IsFeature() )
786 pAttr->GetEnd() = nStart; // dann hoert es hier auf
789 else if ( !pAttr->IsFeature() || ( pAttr->GetStart() == nStart ) )
791 // Feature nur loeschen, wenn genau an der Stelle
792 bRemoveAttrib = TRUE;
795 // Attribut ueberlappt die Selektion
796 else if ( ( pAttr->GetStart() <= nStart ) && ( pAttr->GetEnd() >= nEnd ) )
799 if ( pAttr->GetStart() == nStart )
801 pAttr->GetStart() = nEnd;
803 break; // es kann weitere Attribute geben!
805 else if ( pAttr->GetEnd() == nEnd )
807 pAttr->GetEnd() = nStart;
809 break; // es kann weitere Attribute geben!
811 else // Attribut muss gesplittet werden...
813 USHORT nOldEnd = pAttr->GetEnd();
814 pAttr->GetEnd() = nStart;
816 // ULONG nSavePos = pNode->GetCharAttribs().GetStartList().GetCurPos();
817 InsertAttrib( *pAttr->GetItem(), pNode, nEnd, nOldEnd );
818 // pNode->GetCharAttribs().GetStartList().Seek( nSavePos );
819 break; // es kann weitere Attribute geben!
825 DBG_ASSERT( ( pAttr != rpStarting ) && ( pAttr != rpEnding ), "Loeschen und behalten des gleichen Attributs ?" );
826 pNode->GetCharAttribs().GetAttribs().Remove(nAttr);
827 pCurPool->Remove( *pAttr->GetItem() );
832 pAttr = GetAttrib( pNode->GetCharAttribs().GetAttribs(), nAttr );
837 #pragma SEG_FUNCDEF(editdoc_3f)
839 void TextDoc::InsertAttrib( const SfxPoolItem& rPoolItem, TextNode* pNode, USHORT nStart, USHORT nEnd )
841 // Diese Methode prueft nicht mehr, ob ein entspr. Attribut
842 // schon an der Stelle existiert!
844 // pruefen, ob neues Attrib oder einfach nur Ende eines Attribs...
845 // const SfxPoolItem& rDefItem = pNode->GetContentAttribs().GetItem( rPoolItem.Which() );
846 // BOOL bCreateAttrib = ( rDefItem != rPoolItem );
848 // Durch den Verlust der Exclude-Liste geht es nicht mehr, dass ich
849 // kein neues Attribut benoetige und nur das alte nicht expandiere...
850 // if ( !bCreateAttrib )
852 // => Wenn schon Default-Item, dann wenigstens nur dann einstellen,
853 // wenn davor wirklich ein entsprechendes Attribut.
854 // if ( pNode->GetCharAttribs().FindAttrib( rPoolItem.Which(), nStart ) )
855 // bCreateAttrib = TRUE;
856 // Aber kleiner Trost:
857 // Die wenigsten schreiben, aendern das Attr, schreiben, und
858 // stellen dann wieder das Default-Attr ein.
862 // Die Uberlegung, einfach das andere Attribut nicht zu expandieren, war
863 // sowieso falsch, da das DefAttr aus einer Vorlage kommen kann,
864 // die irgendwann verschwindet!
865 // if ( bCreateAttrib )
867 TextCharAttrib* pAttrib = MakeCharAttrib( *pCurPool, rPoolItem, nStart, nEnd );
868 DBG_ASSERT( pAttrib, "MakeCharAttrib fehlgeschlagen!" );
869 pNode->GetCharAttribs().InsertAttrib( pAttrib );
873 // TextCharAttrib* pTmpAttrib =
874 // pNode->GetCharAttribs().FindAnyAttrib( rPoolItem.Which() );
875 // if ( pTmpAttrib ) // sonst benoetige ich es sowieso nicht....
877 // aExcludeList.Insert( pTmpAttrib->GetItem() );
882 #pragma SEG_FUNCDEF(editdoc_40)
884 void TextDoc::InsertAttrib( TextNode* pNode, USHORT nStart, USHORT nEnd, const SfxPoolItem& rPoolItem )
886 if ( nStart != nEnd )
888 InsertAttribInSelection( pNode, nStart, nEnd, rPoolItem );
892 // Pruefen, ob schon ein neues Attribut mit der WhichId an der Stelle:
893 TextCharAttrib* pAttr = pNode->GetCharAttribs().FindEmptyAttrib( rPoolItem.Which(), nStart );
896 // Attribut entfernen....
897 pNode->GetCharAttribs().GetAttribs().Remove(
898 pNode->GetCharAttribs().GetAttribs().GetPos( pAttr ) );
901 // pruefen, ob ein 'gleiches' Attribut an der Stelle liegt.
902 pAttr = pNode->GetCharAttribs().FindAttrib( rPoolItem.Which(), nStart );
905 if ( pAttr->IsInside( nStart ) ) // splitten
907 // ???????????????????????????????
908 // eigentlich noch pruefen, ob wirklich splittet, oder return !
909 // ???????????????????????????????
910 USHORT nOldEnd = pAttr->GetEnd();
911 pAttr->GetEnd() = nStart;
912 pAttr = MakeCharAttrib( *pCurPool, *(pAttr->GetItem()), nStart, nOldEnd );
913 pNode->GetCharAttribs().InsertAttrib( pAttr );
915 else if ( pAttr->GetEnd() == nStart )
917 DBG_ASSERT( !pAttr->IsEmpty(), "Doch noch ein leeres Attribut?" );
918 // pruefen, ob genau das gleiche Attribut
919 if ( *(pAttr->GetItem()) == rPoolItem )
923 InsertAttrib( rPoolItem, pNode, nStart, nStart );
927 #pragma SEG_FUNCDEF(editdoc_41)
929 void TextDoc::FindAttribs( TextNode* pNode, USHORT nStartPos, USHORT nEndPos, SfxItemSet& rCurSet )
931 DBG_ASSERT( pNode, "Wo soll ich suchen ?" );
932 DBG_ASSERT( nStartPos <= nEndPos, "Ungueltiger Bereich!" );
935 TextCharAttrib* pAttr = GetAttrib( pNode->GetCharAttribs().GetAttribs(), nAttr );
936 // keine Selection...
937 if ( nStartPos == nEndPos )
939 while ( pAttr && ( pAttr->GetStart() <= nEndPos) )
941 const SfxPoolItem* pItem = 0;
942 // Attribut liegt dadrueber...
943 if ( ( pAttr->GetStart() < nStartPos ) && ( pAttr->GetEnd() > nStartPos ) )
944 pItem = pAttr->GetItem();
945 // Attribut endet hier, ist nicht leer
946 else if ( ( pAttr->GetStart() < nStartPos ) && ( pAttr->GetEnd() == nStartPos ) )
948 if ( !pNode->GetCharAttribs().FindEmptyAttrib( pAttr->GetItem()->Which(), nStartPos ) )
949 pItem = pAttr->GetItem();
951 // Attribut endet hier, ist leer
952 else if ( ( pAttr->GetStart() == nStartPos ) && ( pAttr->GetEnd() == nStartPos ) )
954 // if ( aExcludeList.FindAttrib( pAttr->GetItem()->Which() ) )
955 pItem = pAttr->GetItem();
956 // else if ( pNode->Len() == 0 ) // Sonderfall
957 // pItem = pAttr->GetItem();
959 // Attribut beginnt hier
960 else if ( ( pAttr->GetStart() == nStartPos ) && ( pAttr->GetEnd() > nStartPos ) )
962 if ( nStartPos == 0 ) // Sonderfall
963 pItem = pAttr->GetItem();
968 USHORT nWhich = pItem->Which();
969 if ( rCurSet.GetItemState( nWhich ) == SFX_ITEM_OFF )
971 rCurSet.Put( *pItem );
973 else if ( rCurSet.GetItemState( nWhich ) == SFX_ITEM_ON )
975 const SfxPoolItem& rItem = rCurSet.Get( nWhich );
976 if ( rItem != *pItem )
978 rCurSet.InvalidateItem( nWhich );
983 pAttr = GetAttrib( pNode->GetCharAttribs().GetAttribs(), nAttr );
988 while ( pAttr && ( pAttr->GetStart() < nEndPos) )
990 const SfxPoolItem* pItem = 0;
991 // Attribut liegt dadrueber...
992 if ( ( pAttr->GetStart() <= nStartPos ) && ( pAttr->GetEnd() >= nEndPos ) )
993 pItem = pAttr->GetItem();
994 // Attribut startet mitten drin...
995 else if ( pAttr->GetStart() >= nStartPos )
997 // !!! pItem = pAttr->GetItem();
998 // einfach nur pItem reicht nicht, da ich z.B. bei Shadow
999 // niemals ein ungleiches Item finden wuerde, da ein solche
1000 // seine Anwesenheit durch Abwesenheit repraesentiert!
1002 // Es muesste geprueft werden, on genau das gleiche Attribut
1003 // an der Bruchstelle aufsetzt, was recht aufwendig ist.
1004 // Da ich beim Einfuegen von Attributen aber etwas optimiere
1005 // tritt der Fall nicht so schnell auf...
1006 // Also aus Geschwindigkeitsgruenden:
1007 rCurSet.InvalidateItem( pAttr->GetItem()->Which() );
1010 // Attribut endet mitten drin...
1011 else if ( pAttr->GetEnd() > nStartPos )
1013 // pItem = pAttr->GetItem();
1016 // -----------------31.05.95 16:01-------------------
1017 // Ist falsch, wenn das gleiche Attribut sofort wieder
1018 // eingestellt wird!
1019 // => Sollte am besten nicht vorkommen, also gleich beim
1020 // Setzen von Attributen richtig machen!
1021 // --------------------------------------------------
1022 rCurSet.InvalidateItem( pAttr->GetItem()->Which() );
1027 USHORT nWhich = pItem->Which();
1028 if ( rCurSet.GetItemState( nWhich ) == SFX_ITEM_OFF )
1030 rCurSet.Put( *pItem );
1032 else if ( rCurSet.GetItemState( nWhich ) == SFX_ITEM_ON )
1034 const SfxPoolItem& rItem = rCurSet.Get( nWhich );
1035 if ( rItem != *pItem )
1037 rCurSet.InvalidateItem( nWhich );
1042 pAttr = GetAttrib( pNode->GetCharAttribs().GetAttribs(), nAttr );