Line data Source code
1 : /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 : /*
3 : * This file is part of the LibreOffice project.
4 : *
5 : * This Source Code Form is subject to the terms of the Mozilla Public
6 : * License, v. 2.0. If a copy of the MPL was not distributed with this
7 : * file, You can obtain one at http://mozilla.org/MPL/2.0/.
8 : *
9 : * This file incorporates work covered by the following license notice:
10 : *
11 : * Licensed to the Apache Software Foundation (ASF) under one or more
12 : * contributor license agreements. See the NOTICE file distributed
13 : * with this work for additional information regarding copyright
14 : * ownership. The ASF licenses this file to you under the Apache
15 : * License, Version 2.0 (the "License"); you may not use this file
16 : * except in compliance with the License. You may obtain a copy of
17 : * the License at http://www.apache.org/licenses/LICENSE-2.0 .
18 : */
19 :
20 : #include <textdoc.hxx>
21 :
22 : #include <stdlib.h>
23 :
24 :
25 : // compare function called by QuickSort
26 0 : static bool CompareStart( const TextCharAttrib* pFirst, const TextCharAttrib* pSecond )
27 : {
28 0 : return pFirst->GetStart() < pSecond->GetStart();
29 : }
30 :
31 0 : TextCharAttrib::TextCharAttrib( const TextAttrib& rAttr, sal_uInt16 nStart, sal_uInt16 nEnd )
32 : {
33 0 : mpAttr = rAttr.Clone();
34 : mnStart = nStart,
35 0 : mnEnd = nEnd;
36 0 : }
37 :
38 0 : TextCharAttrib::TextCharAttrib( const TextCharAttrib& rTextCharAttrib )
39 : {
40 0 : mpAttr = rTextCharAttrib.GetAttr().Clone();
41 0 : mnStart = rTextCharAttrib.mnStart;
42 0 : mnEnd = rTextCharAttrib.mnEnd;
43 0 : }
44 :
45 0 : TextCharAttrib::~TextCharAttrib()
46 : {
47 0 : delete mpAttr;
48 0 : }
49 :
50 109 : TextCharAttribList::TextCharAttribList()
51 : {
52 109 : mbHasEmptyAttribs = sal_False;
53 109 : }
54 :
55 109 : TextCharAttribList::~TextCharAttribList()
56 : {
57 : // PTRARR_DEL
58 109 : }
59 :
60 0 : void TextCharAttribList::Clear( sal_Bool bDestroyAttribs )
61 : {
62 0 : if ( bDestroyAttribs )
63 0 : for(iterator it = begin(); it != end(); ++it)
64 0 : delete *it;
65 0 : TextCharAttribs::clear();
66 0 : }
67 :
68 :
69 0 : void TextCharAttribList::InsertAttrib( TextCharAttrib* pAttrib )
70 : {
71 0 : if ( pAttrib->IsEmpty() )
72 0 : mbHasEmptyAttribs = sal_True;
73 :
74 0 : const sal_uInt16 nCount = size();
75 0 : const sal_uInt16 nStart = pAttrib->GetStart(); // maybe better for Comp.Opt.
76 0 : bool bInserted = false;
77 0 : for ( sal_uInt16 x = 0; x < nCount; x++ )
78 : {
79 0 : TextCharAttrib* pCurAttrib = GetAttrib( x );
80 0 : if ( pCurAttrib->GetStart() > nStart )
81 : {
82 0 : insert( begin() + x, pAttrib );
83 0 : bInserted = true;
84 0 : break;
85 : }
86 : }
87 0 : if ( !bInserted )
88 0 : push_back( pAttrib );
89 0 : }
90 :
91 0 : void TextCharAttribList::ResortAttribs()
92 : {
93 0 : if ( !empty() )
94 0 : std::sort( begin(), end(), CompareStart );
95 0 : }
96 :
97 0 : TextCharAttrib* TextCharAttribList::FindAttrib( sal_uInt16 nWhich, sal_uInt16 nPos )
98 : {
99 : // backwards; if one ends there and the next starts there
100 : // ==> the starting one counts
101 0 : for ( sal_uInt16 nAttr = size(); nAttr; )
102 : {
103 0 : TextCharAttrib* pAttr = GetAttrib( --nAttr );
104 :
105 0 : if ( pAttr->GetEnd() < nPos )
106 0 : return 0;
107 :
108 0 : if ( ( pAttr->Which() == nWhich ) && pAttr->IsIn(nPos) )
109 0 : return pAttr;
110 : }
111 0 : return NULL;
112 : }
113 :
114 0 : TextCharAttrib* TextCharAttribList::FindNextAttrib( sal_uInt16 nWhich, sal_uInt16 nFromPos, sal_uInt16 nMaxPos ) const
115 : {
116 : DBG_ASSERT( nWhich, "FindNextAttrib: Which?" );
117 0 : const sal_uInt16 nAttribs = size();
118 0 : for ( sal_uInt16 nAttr = 0; nAttr < nAttribs; nAttr++ )
119 : {
120 0 : TextCharAttrib* pAttr = GetAttrib( nAttr );
121 0 : if ( ( pAttr->GetStart() >= nFromPos ) &&
122 0 : ( pAttr->GetEnd() <= nMaxPos ) &&
123 0 : ( pAttr->Which() == nWhich ) )
124 0 : return pAttr;
125 : }
126 0 : return NULL;
127 : }
128 :
129 0 : sal_Bool TextCharAttribList::HasAttrib( sal_uInt16 nWhich ) const
130 : {
131 0 : for ( sal_uInt16 nAttr = size(); nAttr; )
132 : {
133 0 : const TextCharAttrib* pAttr = GetAttrib( --nAttr );
134 0 : if ( pAttr->Which() == nWhich )
135 0 : return sal_True;
136 : }
137 0 : return sal_False;
138 : }
139 :
140 0 : sal_Bool TextCharAttribList::HasBoundingAttrib( sal_uInt16 nBound )
141 : {
142 : // backwards; if one ends there and the next starts there
143 : // ==> the starting one counts
144 0 : for ( sal_uInt16 nAttr = size(); nAttr; )
145 : {
146 0 : TextCharAttrib* pAttr = GetAttrib( --nAttr );
147 :
148 0 : if ( pAttr->GetEnd() < nBound )
149 0 : return sal_False;
150 :
151 0 : if ( ( pAttr->GetStart() == nBound ) || ( pAttr->GetEnd() == nBound ) )
152 0 : return sal_True;
153 : }
154 0 : return sal_False;
155 : }
156 :
157 0 : TextCharAttrib* TextCharAttribList::FindEmptyAttrib( sal_uInt16 nWhich, sal_uInt16 nPos )
158 : {
159 0 : if ( !mbHasEmptyAttribs )
160 0 : return 0;
161 :
162 0 : const sal_uInt16 nAttribs = size();
163 0 : for ( sal_uInt16 nAttr = 0; nAttr < nAttribs; nAttr++ )
164 : {
165 0 : TextCharAttrib* pAttr = GetAttrib( nAttr );
166 0 : if ( pAttr->GetStart() > nPos )
167 0 : return 0;
168 :
169 0 : if ( ( pAttr->GetStart() == nPos ) && ( pAttr->GetEnd() == nPos ) && ( pAttr->Which() == nWhich ) )
170 0 : return pAttr;
171 : }
172 0 : return 0;
173 : }
174 :
175 0 : void TextCharAttribList::DeleteEmptyAttribs()
176 : {
177 0 : for ( sal_uInt16 nAttr = 0; nAttr < size(); nAttr++ )
178 : {
179 0 : TextCharAttrib* pAttr = GetAttrib( nAttr );
180 0 : if ( pAttr->IsEmpty() )
181 : {
182 0 : erase( begin() + nAttr );
183 0 : delete pAttr;
184 0 : nAttr--;
185 : }
186 : }
187 0 : mbHasEmptyAttribs = sal_False;
188 0 : }
189 :
190 109 : TextNode::TextNode( const String& rText ) :
191 109 : maText( rText )
192 : {
193 109 : }
194 :
195 26 : void TextNode::ExpandAttribs( sal_uInt16 nIndex, sal_uInt16 nNew )
196 : {
197 26 : if ( !nNew )
198 26 : return;
199 :
200 26 : bool bResort = false;
201 26 : sal_uInt16 nAttribs = maCharAttribs.Count();
202 26 : for ( sal_uInt16 nAttr = 0; nAttr < nAttribs; nAttr++ )
203 : {
204 0 : TextCharAttrib* pAttrib = maCharAttribs.GetAttrib( nAttr );
205 0 : if ( pAttrib->GetEnd() >= nIndex )
206 : {
207 : // move all attributes that are behind the cursor
208 0 : if ( pAttrib->GetStart() > nIndex )
209 : {
210 0 : pAttrib->MoveForward( nNew );
211 : }
212 : // 0: expand empty attribute, if at cursor
213 0 : else if ( pAttrib->IsEmpty() )
214 : {
215 : // Do not check the index; empty one may only be here.
216 : // If checking later anyway, special case:
217 : // Start == 0; AbsLen == 1, nNew = 1 => Expand due to new paragraph!
218 : // Start <= nIndex, End >= nIndex => Start=End=nIndex!
219 0 : pAttrib->Expand( nNew );
220 : }
221 : // 1: attribute starts before and reaches up to index
222 0 : else if ( pAttrib->GetEnd() == nIndex ) // start must be before
223 : {
224 : // Only expand if no feature and not in Exclude list!
225 : // Otherwise e.g. an UL would go until the new ULDB, thus expand both.
226 0 : if ( !maCharAttribs.FindEmptyAttrib( pAttrib->Which(), nIndex ) )
227 : {
228 0 : pAttrib->Expand( nNew );
229 : }
230 : else
231 0 : bResort = true;
232 : }
233 : // 2: attribute starts before and reaches past the index
234 0 : else if ( ( pAttrib->GetStart() < nIndex ) && ( pAttrib->GetEnd() > nIndex ) )
235 : {
236 0 : pAttrib->Expand( nNew );
237 : }
238 : // 3: attribute starts at Index
239 0 : else if ( pAttrib->GetStart() == nIndex )
240 : {
241 0 : if ( nIndex == 0 )
242 : {
243 0 : pAttrib->Expand( nNew );
244 : }
245 : else
246 0 : pAttrib->MoveForward( nNew );
247 : }
248 : }
249 :
250 : DBG_ASSERT( pAttrib->GetStart() <= pAttrib->GetEnd(), "Expand: Attribut verdreht!" );
251 : DBG_ASSERT( ( pAttrib->GetEnd() <= maText.Len() ), "Expand: Attrib groesser als Absatz!" );
252 : DBG_ASSERT( !pAttrib->IsEmpty(), "Leeres Attribut nach ExpandAttribs?" );
253 : }
254 :
255 26 : if ( bResort )
256 0 : maCharAttribs.ResortAttribs();
257 : }
258 :
259 0 : void TextNode::CollapsAttribs( sal_uInt16 nIndex, sal_uInt16 nDeleted )
260 : {
261 0 : if ( !nDeleted )
262 0 : return;
263 :
264 0 : bool bResort = false;
265 0 : sal_uInt16 nEndChanges = nIndex+nDeleted;
266 :
267 0 : for ( sal_uInt16 nAttr = 0; nAttr < maCharAttribs.Count(); nAttr++ )
268 : {
269 0 : TextCharAttrib* pAttrib = maCharAttribs.GetAttrib( nAttr );
270 0 : bool bDelAttr = false;
271 0 : if ( pAttrib->GetEnd() >= nIndex )
272 : {
273 : // move all attributes that are behind the cursor
274 0 : if ( pAttrib->GetStart() >= nEndChanges )
275 : {
276 0 : pAttrib->MoveBackward( nDeleted );
277 : }
278 : // 1. delete inner attributes
279 0 : else if ( ( pAttrib->GetStart() >= nIndex ) && ( pAttrib->GetEnd() <= nEndChanges ) )
280 : {
281 : // special case: attribute covers the region exactly
282 : // => keep as an empty attribute
283 0 : if ( ( pAttrib->GetStart() == nIndex ) && ( pAttrib->GetEnd() == nEndChanges ) )
284 0 : pAttrib->GetEnd() = nIndex; // empty
285 : else
286 0 : bDelAttr = true;
287 : }
288 : // 2. attribute starts before, ends inside or after
289 0 : else if ( ( pAttrib->GetStart() <= nIndex ) && ( pAttrib->GetEnd() > nIndex ) )
290 : {
291 0 : if ( pAttrib->GetEnd() <= nEndChanges ) // ends inside
292 0 : pAttrib->GetEnd() = nIndex;
293 : else
294 0 : pAttrib->Collaps( nDeleted ); // ends after
295 : }
296 : // 3. attribute starts inside, ends after
297 0 : else if ( ( pAttrib->GetStart() >= nIndex ) && ( pAttrib->GetEnd() > nEndChanges ) )
298 : {
299 : // features are not allowed to expand!
300 0 : pAttrib->GetStart() = nEndChanges;
301 0 : pAttrib->MoveBackward( nDeleted );
302 : }
303 : }
304 :
305 : DBG_ASSERT( pAttrib->GetStart() <= pAttrib->GetEnd(), "Collaps: Attribut verdreht!" );
306 : DBG_ASSERT( ( pAttrib->GetEnd() <= maText.Len()) || bDelAttr, "Collaps: Attrib groesser als Absatz!" );
307 0 : if ( bDelAttr /* || pAttrib->IsEmpty() */ )
308 : {
309 0 : bResort = true;
310 0 : maCharAttribs.RemoveAttrib( nAttr );
311 0 : delete pAttrib;
312 0 : nAttr--;
313 : }
314 0 : else if ( pAttrib->IsEmpty() )
315 0 : maCharAttribs.HasEmptyAttribs() = sal_True;
316 : }
317 :
318 0 : if ( bResort )
319 0 : maCharAttribs.ResortAttribs();
320 : }
321 :
322 26 : void TextNode::InsertText( sal_uInt16 nPos, const String& rText )
323 : {
324 26 : maText.Insert( rText, nPos );
325 26 : ExpandAttribs( nPos, rText.Len() );
326 26 : }
327 :
328 0 : void TextNode::InsertText( sal_uInt16 nPos, sal_Unicode c )
329 : {
330 0 : maText.Insert( c, nPos );
331 0 : ExpandAttribs( nPos, 1 );
332 0 : }
333 :
334 0 : void TextNode::RemoveText( sal_uInt16 nPos, sal_uInt16 nChars )
335 : {
336 0 : maText.Erase( nPos, nChars );
337 0 : CollapsAttribs( nPos, nChars );
338 0 : }
339 :
340 0 : TextNode* TextNode::Split( sal_uInt16 nPos, sal_Bool bKeepEndingAttribs )
341 : {
342 0 : String aNewText;
343 0 : if ( nPos < maText.Len() )
344 : {
345 0 : aNewText = maText.Copy( nPos );
346 0 : maText.Erase( nPos );
347 : }
348 0 : TextNode* pNew = new TextNode( aNewText );
349 :
350 0 : for ( sal_uInt16 nAttr = 0; nAttr < maCharAttribs.Count(); nAttr++ )
351 : {
352 0 : TextCharAttrib* pAttrib = maCharAttribs.GetAttrib( nAttr );
353 0 : if ( pAttrib->GetEnd() < nPos )
354 : {
355 : // no change
356 : ;
357 : }
358 0 : else if ( pAttrib->GetEnd() == nPos )
359 : {
360 : // must be copied as an empty attribute
361 : // !FindAttrib only sensible if traversing backwards through the list!
362 0 : if ( bKeepEndingAttribs && !pNew->maCharAttribs.FindAttrib( pAttrib->Which(), 0 ) )
363 : {
364 0 : TextCharAttrib* pNewAttrib = new TextCharAttrib( *pAttrib );
365 0 : pNewAttrib->GetStart() = 0;
366 0 : pNewAttrib->GetEnd() = 0;
367 0 : pNew->maCharAttribs.InsertAttrib( pNewAttrib );
368 : }
369 : }
370 0 : else if ( pAttrib->IsInside( nPos ) || ( !nPos && !pAttrib->GetStart() ) )
371 : {
372 : // If cutting at the very beginning, the attribute has to be
373 : // copied and changed
374 0 : TextCharAttrib* pNewAttrib = new TextCharAttrib( *pAttrib );
375 0 : pNewAttrib->GetStart() = 0;
376 0 : pNewAttrib->GetEnd() = pAttrib->GetEnd()-nPos;
377 0 : pNew->maCharAttribs.InsertAttrib( pNewAttrib );
378 : // trim
379 0 : pAttrib->GetEnd() = nPos;
380 : }
381 : else
382 : {
383 : DBG_ASSERT( pAttrib->GetStart() >= nPos, "Start < nPos!" );
384 : DBG_ASSERT( pAttrib->GetEnd() >= nPos, "End < nPos!" );
385 : // move all into the new node (this)
386 0 : maCharAttribs.RemoveAttrib( nAttr );
387 0 : pNew->maCharAttribs.InsertAttrib( pAttrib );
388 0 : pAttrib->GetStart() = pAttrib->GetStart() - nPos;
389 0 : pAttrib->GetEnd() = pAttrib->GetEnd() - nPos;
390 0 : nAttr--;
391 : }
392 : }
393 0 : return pNew;
394 : }
395 :
396 0 : void TextNode::Append( const TextNode& rNode )
397 : {
398 0 : sal_uInt16 nOldLen = maText.Len();
399 :
400 0 : maText += rNode.GetText();
401 :
402 0 : const sal_uInt16 nAttribs = rNode.GetCharAttribs().Count();
403 0 : for ( sal_uInt16 nAttr = 0; nAttr < nAttribs; nAttr++ )
404 : {
405 0 : TextCharAttrib* pAttrib = rNode.GetCharAttribs().GetAttrib( nAttr );
406 0 : bool bMelted = false;
407 0 : if ( pAttrib->GetStart() == 0 )
408 : {
409 : // potentially merge attributes
410 0 : sal_uInt16 nTmpAttribs = maCharAttribs.Count();
411 0 : for ( sal_uInt16 nTmpAttr = 0; nTmpAttr < nTmpAttribs; nTmpAttr++ )
412 : {
413 0 : TextCharAttrib* pTmpAttrib = maCharAttribs.GetAttrib( nTmpAttr );
414 :
415 0 : if ( pTmpAttrib->GetEnd() == nOldLen )
416 : {
417 0 : if ( ( pTmpAttrib->Which() == pAttrib->Which() ) &&
418 0 : ( pTmpAttrib->GetAttr() == pAttrib->GetAttr() ) )
419 : {
420 0 : pTmpAttrib->GetEnd() =
421 0 : pTmpAttrib->GetEnd() + pAttrib->GetLen();
422 0 : bMelted = true;
423 0 : break; // there can be only one of this type at this position
424 : }
425 : }
426 : }
427 : }
428 :
429 0 : if ( !bMelted )
430 : {
431 0 : TextCharAttrib* pNewAttrib = new TextCharAttrib( *pAttrib );
432 0 : pNewAttrib->GetStart() = pNewAttrib->GetStart() + nOldLen;
433 0 : pNewAttrib->GetEnd() = pNewAttrib->GetEnd() + nOldLen;
434 0 : maCharAttribs.InsertAttrib( pNewAttrib );
435 : }
436 : }
437 0 : }
438 :
439 45 : TextDoc::TextDoc()
440 : {
441 45 : mnLeftMargin = 0;
442 45 : };
443 :
444 90 : TextDoc::~TextDoc()
445 : {
446 45 : DestroyTextNodes();
447 45 : }
448 :
449 64 : void TextDoc::Clear()
450 : {
451 64 : DestroyTextNodes();
452 64 : }
453 :
454 109 : void TextDoc::DestroyTextNodes()
455 : {
456 218 : for ( sal_uLong nNode = 0; nNode < maTextNodes.Count(); nNode++ )
457 109 : delete maTextNodes.GetObject( nNode );
458 109 : maTextNodes.clear();
459 109 : }
460 :
461 0 : String TextDoc::GetText( const sal_Unicode* pSep ) const
462 : {
463 0 : sal_uLong nLen = GetTextLen( pSep );
464 0 : sal_uLong nNodes = maTextNodes.Count();
465 :
466 0 : if ( nLen > STRING_MAXLEN )
467 : {
468 : OSL_FAIL( "Text zu gross fuer String" );
469 0 : return String();
470 : }
471 :
472 0 : String aASCIIText;
473 0 : sal_uLong nLastNode = nNodes-1;
474 0 : for ( sal_uLong nNode = 0; nNode < nNodes; nNode++ )
475 : {
476 0 : TextNode* pNode = maTextNodes.GetObject( nNode );
477 0 : String aTmp( pNode->GetText() );
478 0 : aASCIIText += aTmp;
479 0 : if ( pSep && ( nNode != nLastNode ) )
480 0 : aASCIIText += pSep;
481 0 : }
482 :
483 0 : return aASCIIText;
484 : }
485 :
486 0 : XubString TextDoc::GetText( sal_uLong nPara ) const
487 : {
488 0 : XubString aText;
489 0 : TextNode* pNode = ( nPara < maTextNodes.Count() ) ? maTextNodes.GetObject( nPara ) : 0;
490 0 : if ( pNode )
491 0 : aText = pNode->GetText();
492 :
493 0 : return aText;
494 : }
495 :
496 :
497 0 : sal_uLong TextDoc::GetTextLen( const sal_Unicode* pSep, const TextSelection* pSel ) const
498 : {
499 0 : sal_uLong nLen = 0;
500 0 : sal_uLong nNodes = maTextNodes.Count();
501 0 : if ( nNodes )
502 : {
503 0 : sal_uLong nStartNode = 0;
504 0 : sal_uLong nEndNode = nNodes-1;
505 0 : if ( pSel )
506 : {
507 0 : nStartNode = pSel->GetStart().GetPara();
508 0 : nEndNode = pSel->GetEnd().GetPara();
509 : }
510 :
511 0 : for ( sal_uLong nNode = nStartNode; nNode <= nEndNode; nNode++ )
512 : {
513 0 : TextNode* pNode = maTextNodes.GetObject( nNode );
514 :
515 0 : sal_uInt16 nS = 0;
516 0 : sal_uLong nE = pNode->GetText().Len();
517 0 : if ( pSel && ( nNode == pSel->GetStart().GetPara() ) )
518 0 : nS = pSel->GetStart().GetIndex();
519 0 : if ( pSel && ( nNode == pSel->GetEnd().GetPara() ) )
520 0 : nE = pSel->GetEnd().GetIndex();
521 :
522 0 : nLen += ( nE - nS );
523 : }
524 :
525 0 : if ( pSep )
526 0 : nLen += (nEndNode-nStartNode) * rtl_ustr_getLength(pSep);
527 : }
528 :
529 0 : return nLen;
530 : }
531 :
532 0 : TextPaM TextDoc::InsertText( const TextPaM& rPaM, sal_Unicode c )
533 : {
534 : DBG_ASSERT( c != 0x0A, "TextDoc::InsertText: Zeilentrenner in Absatz nicht erlaubt!" );
535 : DBG_ASSERT( c != 0x0D, "TextDoc::InsertText: Zeilentrenner in Absatz nicht erlaubt!" );
536 :
537 0 : TextNode* pNode = maTextNodes.GetObject( rPaM.GetPara() );
538 0 : pNode->InsertText( rPaM.GetIndex(), c );
539 :
540 0 : TextPaM aPaM( rPaM.GetPara(), rPaM.GetIndex()+1 );
541 0 : return aPaM;
542 : }
543 :
544 26 : TextPaM TextDoc::InsertText( const TextPaM& rPaM, const XubString& rStr )
545 : {
546 : DBG_ASSERT( rStr.Search( 0x0A ) == STRING_NOTFOUND, "TextDoc::InsertText: Zeilentrenner in Absatz nicht erlaubt!" );
547 : DBG_ASSERT( rStr.Search( 0x0D ) == STRING_NOTFOUND, "TextDoc::InsertText: Zeilentrenner in Absatz nicht erlaubt!" );
548 :
549 26 : TextNode* pNode = maTextNodes.GetObject( rPaM.GetPara() );
550 26 : pNode->InsertText( rPaM.GetIndex(), rStr );
551 :
552 26 : TextPaM aPaM( rPaM.GetPara(), rPaM.GetIndex()+rStr.Len() );
553 26 : return aPaM;
554 : }
555 :
556 0 : TextPaM TextDoc::InsertParaBreak( const TextPaM& rPaM, sal_Bool bKeepEndingAttribs )
557 : {
558 0 : TextNode* pNode = maTextNodes.GetObject( rPaM.GetPara() );
559 0 : TextNode* pNew = pNode->Split( rPaM.GetIndex(), bKeepEndingAttribs );
560 :
561 0 : maTextNodes.Insert( pNew, rPaM.GetPara()+1 );
562 :
563 0 : TextPaM aPaM( rPaM.GetPara()+1, 0 );
564 0 : return aPaM;
565 : }
566 :
567 0 : TextPaM TextDoc::ConnectParagraphs( TextNode* pLeft, TextNode* pRight )
568 : {
569 0 : sal_uInt16 nPrevLen = pLeft->GetText().Len();
570 0 : pLeft->Append( *pRight );
571 :
572 : // the paragraph on the right vanishes
573 0 : sal_uLong nRight = maTextNodes.GetPos( pRight );
574 0 : maTextNodes.Remove( nRight );
575 0 : delete pRight;
576 :
577 0 : sal_uLong nLeft = maTextNodes.GetPos( pLeft );
578 0 : TextPaM aPaM( nLeft, nPrevLen );
579 0 : return aPaM;
580 : }
581 :
582 0 : TextPaM TextDoc::RemoveChars( const TextPaM& rPaM, sal_uInt16 nChars )
583 : {
584 0 : TextNode* pNode = maTextNodes.GetObject( rPaM.GetPara() );
585 0 : pNode->RemoveText( rPaM.GetIndex(), nChars );
586 :
587 0 : return rPaM;
588 : }
589 :
590 0 : sal_Bool TextDoc::IsValidPaM( const TextPaM& rPaM )
591 : {
592 0 : if ( rPaM.GetPara() >= maTextNodes.Count() )
593 : {
594 : OSL_FAIL( "PaM: Para out of range" );
595 0 : return sal_False;
596 : }
597 0 : TextNode * pNode = maTextNodes.GetObject( rPaM.GetPara() );
598 0 : if ( rPaM.GetIndex() > pNode->GetText().Len() )
599 : {
600 : OSL_FAIL( "PaM: Index out of range" );
601 0 : return sal_False;
602 : }
603 0 : return sal_True;
604 : }
605 :
606 : /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
|