update dev300-m57
[ooovba.git] / svtools / source / edit / textdoc.cxx
blob8b36ad9081c5aa344a031b163125052fada126f3
1 /*************************************************************************
3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4 *
5 * Copyright 2008 by Sun Microsystems, Inc.
7 * OpenOffice.org - a multi-platform office productivity suite
9 * $RCSfile: textdoc.cxx,v $
10 * $Revision: 1.6 $
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>
36 #include <stdlib.h>
38 SV_IMPL_PTRARR( TextCharAttribs, TextCharAttribPtr );
42 // Vergleichmethode wird von QuickSort gerufen...
44 EXTERN_C
45 #if defined( PM2 ) && (!defined( CSET ) && !defined ( MTW ) && !defined( WTC ))
46 int _stdcall
47 #else
48 #ifdef WNT
49 #if _MSC_VER >= 1200
50 int __cdecl
51 #else
52 int _cdecl
53 #endif
54 #else
55 int
56 #endif
57 #endif
59 CompareStart( const void* pFirst, const void* pSecond )
61 if ( (*((TextCharAttrib**)pFirst))->GetStart() < (*((TextCharAttrib**)pSecond))->GetStart() )
62 return (-1);
63 else if ( (*((TextCharAttrib**)pFirst))->GetStart() > (*((TextCharAttrib**)pSecond))->GetStart() )
64 return (1);
65 return 0;
69 \f// -------------------------------------------------------------------------
70 // (+) class TextCharAttrib
71 // -------------------------------------------------------------------------
72 TextCharAttrib::TextCharAttrib( const TextAttrib& rAttr, USHORT nStart, USHORT nEnd )
74 mpAttr = rAttr.Clone();
75 mnStart = nStart,
76 mnEnd = nEnd;
79 TextCharAttrib::TextCharAttrib( const TextCharAttrib& rTextCharAttrib )
81 mpAttr = rTextCharAttrib.GetAttr().Clone();
82 mnStart = rTextCharAttrib.mnStart;
83 mnEnd = rTextCharAttrib.mnEnd;
86 TextCharAttrib::~TextCharAttrib()
88 delete mpAttr;
91 \f// -------------------------------------------------------------------------
92 // (+) class TextCharAttribList
93 // -------------------------------------------------------------------------
95 TextCharAttribList::TextCharAttribList()
97 mbHasEmptyAttribs = FALSE;
100 TextCharAttribList::~TextCharAttribList()
102 // PTRARR_DEL
105 void TextCharAttribList::Clear( BOOL bDestroyAttribs )
107 if ( bDestroyAttribs )
108 TextCharAttribs::DeleteAndDestroy( 0, Count() );
109 else
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 );
128 bInserted = TRUE;
129 break;
132 if ( !bInserted )
133 Insert( pAttrib, nCount );
136 void TextCharAttribList::ResortAttribs()
138 if ( Count() )
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 )
152 return 0;
154 if ( ( pAttr->Which() == nWhich ) && pAttr->IsIn(nPos) )
155 return pAttr;
157 return NULL;
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 ) )
170 return pAttr;
172 return NULL;
175 BOOL TextCharAttribList::HasAttrib( USHORT nWhich ) const
177 for ( USHORT nAttr = Count(); nAttr; )
179 const TextCharAttrib* pAttr = GetObject( --nAttr );
180 if ( pAttr->Which() == nWhich )
181 return TRUE;
183 return FALSE;
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 )
195 return FALSE;
197 if ( ( pAttr->GetStart() == nBound ) || ( pAttr->GetEnd() == nBound ) )
198 return TRUE;
200 return FALSE;
203 TextCharAttrib* TextCharAttribList::FindEmptyAttrib( USHORT nWhich, USHORT nPos )
205 if ( !mbHasEmptyAttribs )
206 return 0;
208 const USHORT nAttribs = Count();
209 for ( USHORT nAttr = 0; nAttr < nAttribs; nAttr++ )
211 TextCharAttrib* pAttr = GetObject( nAttr );
212 if ( pAttr->GetStart() > nPos )
213 return 0;
215 if ( ( pAttr->GetStart() == nPos ) && ( pAttr->GetEnd() == nPos ) && ( pAttr->Which() == nWhich ) )
216 return pAttr;
218 return 0;
221 void TextCharAttribList::DeleteEmptyAttribs()
223 for ( USHORT nAttr = 0; nAttr < Count(); nAttr++ )
225 TextCharAttrib* pAttr = GetObject( nAttr );
226 if ( pAttr->IsEmpty() )
228 Remove( nAttr );
229 delete pAttr;
230 nAttr--;
233 mbHasEmptyAttribs = FALSE;
236 #ifdef DBG_UTIL
237 BOOL TextCharAttribList::DbgCheckAttribs()
239 BOOL bOK = TRUE;
240 for ( USHORT nAttr = 0; nAttr < Count(); nAttr++ )
242 TextCharAttrib* pAttr = GetObject( nAttr );
243 if ( pAttr->GetStart() > pAttr->GetEnd() )
245 bOK = FALSE;
246 DBG_ERROR( "Attr verdreht" );
249 return bOK;
251 #endif
253 \f// -------------------------------------------------------------------------
254 // (+) class TextNode
255 // -------------------------------------------------------------------------
257 TextNode::TextNode( const String& rText ) :
258 maText( rText )
262 void TextNode::ExpandAttribs( USHORT nIndex, USHORT nNew )
264 if ( !nNew )
265 return;
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 );
299 else
300 bResort = TRUE;
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 )
310 if ( nIndex == 0 )
312 pAttrib->Expand( nNew );
313 // bResort = TRUE; // es gibt ja keine Features mehr...
315 else
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?" );
325 if ( bResort )
326 maCharAttribs.ResortAttribs();
328 #ifdef EDITDEBUG
329 DBG_ASSERT( CheckOrderedList( (TextCharAttribs*)&maCharAttribs ), "Expand: Start-Liste verdreht" );
330 #endif
333 void TextNode::CollapsAttribs( USHORT nIndex, USHORT nDeleted )
335 if ( !nDeleted )
336 return;
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
359 else
360 bDelAttr = TRUE;
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;
367 else
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() */ )
383 bResort = TRUE;
384 maCharAttribs.RemoveAttrib( nAttr );
385 delete pAttrib;
386 nAttr--;
388 else if ( pAttrib->IsEmpty() )
389 maCharAttribs.HasEmptyAttribs() = TRUE;
392 if ( bResort )
393 maCharAttribs.ResortAttribs();
395 #ifdef EDITDEBUG
396 DBG_ASSERT( CheckOrderedList( (TextCharAttribs)&maCharAttribs ), "Collaps: Start-Liste verdreht" );
397 #endif
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 )
420 String aNewText;
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 );
456 // stutzen:
457 pAttrib->GetEnd() = nPos;
459 else
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;
468 nAttr--;
471 return pNew;
474 void TextNode::Append( const TextNode& rNode )
476 USHORT nOldLen = maText.Len();
478 maText += rNode.GetText();
480 #ifdef EDITDEBUG
481 DBG_ASSERT( maCharAttribs.DbgCheckAttribs(), "Attribute VOR AppendAttribs kaputt" );
482 #endif
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();
504 bMelted = TRUE;
505 break; // es kann nur eins von der Sorte an der Stelle geben
511 if ( !bMelted )
513 TextCharAttrib* pNewAttrib = new TextCharAttrib( *pAttrib );
514 pNewAttrib->GetStart() = pNewAttrib->GetStart() + nOldLen;
515 pNewAttrib->GetEnd() = pNewAttrib->GetEnd() + nOldLen;
516 maCharAttribs.InsertAttrib( pNewAttrib );
520 #ifdef EDITDEBUG
521 DBG_ASSERT( maCharAttribs.DbgCheckAttribs(), "Attribute NACH AppendAttribs kaputt" );
522 #endif
525 \f// -------------------------------------------------------------------------
526 // (+) class TextDoc
527 // -------------------------------------------------------------------------
529 TextDoc::TextDoc()
531 mnLeftMargin = 0;
534 TextDoc::~TextDoc()
536 DestroyTextNodes();
539 void TextDoc::Clear()
541 DestroyTextNodes();
544 void TextDoc::DestroyTextNodes()
546 for ( ULONG nNode = 0; nNode < maTextNodes.Count(); nNode++ )
547 delete maTextNodes.GetObject( nNode );
548 maTextNodes.clear();
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" );
559 return String();
562 String aASCIIText;
563 ULONG nLastNode = nNodes-1;
564 for ( ULONG nNode = 0; nNode < nNodes; nNode++ )
566 TextNode* pNode = maTextNodes.GetObject( nNode );
567 String aTmp( pNode->GetText() );
568 aASCIIText += aTmp;
569 if ( pSep && ( nNode != nLastNode ) )
570 aASCIIText += pSep;
573 return aASCIIText;
576 XubString TextDoc::GetText( ULONG nPara ) const
578 XubString aText;
579 TextNode* pNode = ( nPara < maTextNodes.Count() ) ? maTextNodes.GetObject( nPara ) : 0;
580 if ( pNode )
581 aText = pNode->GetText();
583 return aText;
587 ULONG TextDoc::GetTextLen( const xub_Unicode* pSep, const TextSelection* pSel ) const
589 ULONG nLen = 0;
590 ULONG nNodes = maTextNodes.Count();
591 if ( nNodes )
593 ULONG nStartNode = 0;
594 ULONG nEndNode = nNodes-1;
595 if ( pSel )
597 nStartNode = pSel->GetStart().GetPara();
598 nEndNode = pSel->GetEnd().GetPara();
601 for ( ULONG nNode = nStartNode; nNode <= nEndNode; nNode++ )
603 TextNode* pNode = maTextNodes.GetObject( nNode );
605 USHORT nS = 0;
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();
612 nLen += ( nE - nS );
615 if ( pSep )
616 nLen += (nEndNode-nStartNode) * String( pSep ).Len();
619 return nLen;
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 );
631 return aPaM;
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() );
643 return aPaM;
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 );
654 return aPaM;
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 );
665 delete pRight;
667 ULONG nLeft = maTextNodes.GetPos( pLeft );
668 TextPaM aPaM( nLeft, nPrevLen );
669 return aPaM;
672 TextPaM TextDoc::RemoveChars( const TextPaM& rPaM, USHORT nChars )
674 TextNode* pNode = maTextNodes.GetObject( rPaM.GetPara() );
675 pNode->RemoveText( rPaM.GetIndex(), nChars );
677 return rPaM;
680 BOOL TextDoc::IsValidPaM( const TextPaM& rPaM )
682 if ( rPaM.GetPara() >= maTextNodes.Count() )
684 DBG_ERROR( "PaM: Para out of range" );
685 return FALSE;
687 TextNode * pNode = maTextNodes.GetObject( rPaM.GetPara() );
688 if ( rPaM.GetIndex() > pNode->GetText().Len() )
690 DBG_ERROR( "PaM: Index out of range" );
691 return FALSE;
693 return TRUE;
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!" );
703 // fuer Optimierung:
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;
727 else
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
747 rpEnding = 0;
748 // dieses startet am Ende der Selektion => kann erweitert werden
749 rpStarting = 0;
751 BOOL bChanged = FALSE;
753 DBG_ASSERT( nStart <= nEnd, "Kleiner Rechenfehler in InsertAttribInSelection" );
755 // ueber die Attribute iterieren...
756 USHORT nAttr = 0;
757 TextCharAttrib* pAttr = GetAttrib( pNode->GetCharAttribs().GetAttribs(), nAttr );
758 while ( pAttr )
760 BOOL bRemoveAttrib = FALSE;
761 if ( !nWhich || ( pAttr->Which() == nWhich ) )
763 // Attribut beginnt in Selection
764 if ( ( pAttr->GetStart() >= nStart ) && ( pAttr->GetStart() <= nEnd ) )
766 bChanged = TRUE;
767 if ( pAttr->GetEnd() > nEnd )
769 pAttr->GetStart() = nEnd; // dann faengt es dahinter an
770 rpStarting = pAttr;
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 ) )
783 bChanged = TRUE;
784 if ( ( pAttr->GetStart() < nStart ) && !pAttr->IsFeature() )
786 pAttr->GetEnd() = nStart; // dann hoert es hier auf
787 rpEnding = pAttr;
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 ) )
798 bChanged = TRUE;
799 if ( pAttr->GetStart() == nStart )
801 pAttr->GetStart() = nEnd;
802 rpStarting = pAttr;
803 break; // es kann weitere Attribute geben!
805 else if ( pAttr->GetEnd() == nEnd )
807 pAttr->GetEnd() = nStart;
808 rpEnding = pAttr;
809 break; // es kann weitere Attribute geben!
811 else // Attribut muss gesplittet werden...
813 USHORT nOldEnd = pAttr->GetEnd();
814 pAttr->GetEnd() = nStart;
815 rpEnding = pAttr;
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!
823 if ( bRemoveAttrib )
825 DBG_ASSERT( ( pAttr != rpStarting ) && ( pAttr != rpEnding ), "Loeschen und behalten des gleichen Attributs ?" );
826 pNode->GetCharAttribs().GetAttribs().Remove(nAttr);
827 pCurPool->Remove( *pAttr->GetItem() );
828 delete pAttr;
829 nAttr--;
831 nAttr++;
832 pAttr = GetAttrib( pNode->GetCharAttribs().GetAttribs(), nAttr );
834 return bChanged;
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.
861 // 22.9.95:
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 )
866 // {
867 TextCharAttrib* pAttrib = MakeCharAttrib( *pCurPool, rPoolItem, nStart, nEnd );
868 DBG_ASSERT( pAttrib, "MakeCharAttrib fehlgeschlagen!" );
869 pNode->GetCharAttribs().InsertAttrib( pAttrib );
870 // }
871 // else
872 // {
873 // TextCharAttrib* pTmpAttrib =
874 // pNode->GetCharAttribs().FindAnyAttrib( rPoolItem.Which() );
875 // if ( pTmpAttrib ) // sonst benoetige ich es sowieso nicht....
876 // {
877 // aExcludeList.Insert( pTmpAttrib->GetItem() );
878 // }
879 // }
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 );
890 else
892 // Pruefen, ob schon ein neues Attribut mit der WhichId an der Stelle:
893 TextCharAttrib* pAttr = pNode->GetCharAttribs().FindEmptyAttrib( rPoolItem.Which(), nStart );
894 if ( pAttr )
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 );
903 if ( pAttr )
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 )
920 return;
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!" );
934 USHORT nAttr = 0;
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();
966 if ( pItem )
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 );
982 nAttr++;
983 pAttr = GetAttrib( pNode->GetCharAttribs().GetAttribs(), nAttr );
986 else // Selektion
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!
1001 // if ( ... )
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();
1014 // s.o.
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() );
1025 if ( pItem )
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 );
1041 nAttr++;
1042 pAttr = GetAttrib( pNode->GetCharAttribs().GetAttribs(), nAttr );