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 : #include <stdlib.h>
22 : #include <boost/mem_fn.hpp>
23 : #include <osl/diagnose.h>
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 181 : TextCharAttribList::TextCharAttribList()
51 : {
52 181 : mbHasEmptyAttribs = false;
53 181 : }
54 :
55 179 : TextCharAttribList::~TextCharAttribList()
56 : {
57 : // PTRARR_DEL
58 179 : }
59 :
60 0 : void TextCharAttribList::Clear()
61 : {
62 0 : maAttribs.clear();
63 0 : }
64 :
65 0 : void TextCharAttribList::InsertAttrib( TextCharAttrib* pAttrib )
66 : {
67 0 : if ( pAttrib->IsEmpty() )
68 0 : mbHasEmptyAttribs = true;
69 :
70 0 : const sal_uInt16 nStart = pAttrib->GetStart(); // maybe better for Comp.Opt.
71 0 : bool bInserted = false;
72 0 : for (TextCharAttribs::iterator it = maAttribs.begin(); it != maAttribs.end(); ++it)
73 : {
74 0 : if ( it->GetStart() > nStart )
75 : {
76 0 : maAttribs.insert( it, pAttrib );
77 0 : bInserted = true;
78 0 : break;
79 : }
80 : }
81 0 : if ( !bInserted )
82 0 : maAttribs.push_back( pAttrib );
83 0 : }
84 :
85 0 : void TextCharAttribList::ResortAttribs()
86 : {
87 0 : maAttribs.sort(CompareStart);
88 0 : }
89 :
90 0 : TextCharAttrib* TextCharAttribList::FindAttrib( sal_uInt16 nWhich, sal_uInt16 nPos )
91 : {
92 0 : for (TextCharAttribs::reverse_iterator it = maAttribs.rbegin(); it != maAttribs.rend(); ++it)
93 : {
94 0 : if ( it->GetEnd() < nPos )
95 0 : return 0;
96 :
97 0 : if ( ( it->Which() == nWhich ) && it->IsIn(nPos) )
98 0 : return &*it;
99 : }
100 0 : return NULL;
101 : }
102 :
103 0 : const TextCharAttrib* TextCharAttribList::FindNextAttrib( sal_uInt16 nWhich, sal_uInt16 nFromPos, sal_uInt16 nMaxPos ) const
104 : {
105 : DBG_ASSERT( nWhich, "FindNextAttrib: Which?" );
106 0 : for (TextCharAttribs::const_iterator it = maAttribs.begin(); it != maAttribs.end(); ++it)
107 : {
108 0 : if ( ( it->GetStart() >= nFromPos ) &&
109 0 : ( it->GetEnd() <= nMaxPos ) &&
110 0 : ( it->Which() == nWhich ) )
111 0 : return &*it;
112 : }
113 0 : return NULL;
114 : }
115 :
116 0 : bool TextCharAttribList::HasAttrib( sal_uInt16 nWhich ) const
117 : {
118 0 : for (TextCharAttribs::const_reverse_iterator it = maAttribs.rbegin(); it != maAttribs.rend(); ++it)
119 : {
120 0 : if ( it->Which() == nWhich )
121 0 : return true;
122 : }
123 0 : return false;
124 : }
125 :
126 0 : bool TextCharAttribList::HasBoundingAttrib( sal_uInt16 nBound )
127 : {
128 0 : for (TextCharAttribs::reverse_iterator it = maAttribs.rbegin(); it != maAttribs.rend(); ++it)
129 : {
130 0 : if ( it->GetEnd() < nBound )
131 0 : return false;
132 :
133 0 : if ( ( it->GetStart() == nBound ) || ( it->GetEnd() == nBound ) )
134 0 : return true;
135 : }
136 0 : return false;
137 : }
138 :
139 0 : TextCharAttrib* TextCharAttribList::FindEmptyAttrib( sal_uInt16 nWhich, sal_uInt16 nPos )
140 : {
141 0 : if ( !mbHasEmptyAttribs )
142 0 : return 0;
143 :
144 0 : for (TextCharAttribs::iterator it = maAttribs.begin(); it != maAttribs.end(); ++it)
145 : {
146 0 : if ( it->GetStart() > nPos )
147 0 : return 0;
148 :
149 0 : if ( ( it->GetStart() == nPos ) && ( it->GetEnd() == nPos ) && ( it->Which() == nWhich ) )
150 0 : return &*it;
151 : }
152 0 : return 0;
153 : }
154 :
155 0 : void TextCharAttribList::DeleteEmptyAttribs()
156 : {
157 0 : maAttribs.erase_if(boost::mem_fn(&TextCharAttrib::IsEmpty));
158 0 : mbHasEmptyAttribs = false;
159 0 : }
160 :
161 181 : TextNode::TextNode( const OUString& rText ) :
162 181 : maText( rText )
163 : {
164 181 : }
165 :
166 42 : void TextNode::ExpandAttribs( sal_uInt16 nIndex, sal_uInt16 nNew )
167 : {
168 42 : if ( !nNew )
169 42 : return;
170 :
171 42 : bool bResort = false;
172 42 : sal_uInt16 nAttribs = maCharAttribs.Count();
173 42 : for ( sal_uInt16 nAttr = 0; nAttr < nAttribs; nAttr++ )
174 : {
175 0 : TextCharAttrib* pAttrib = maCharAttribs.GetAttrib( nAttr );
176 0 : if ( pAttrib->GetEnd() >= nIndex )
177 : {
178 : // move all attributes that are behind the cursor
179 0 : if ( pAttrib->GetStart() > nIndex )
180 : {
181 0 : pAttrib->MoveForward( nNew );
182 : }
183 : // 0: expand empty attribute, if at cursor
184 0 : else if ( pAttrib->IsEmpty() )
185 : {
186 : // Do not check the index; empty one may only be here.
187 : // If checking later anyway, special case:
188 : // Start == 0; AbsLen == 1, nNew = 1 => Expand due to new paragraph!
189 : // Start <= nIndex, End >= nIndex => Start=End=nIndex!
190 0 : pAttrib->Expand( nNew );
191 : }
192 : // 1: attribute starts before and reaches up to index
193 0 : else if ( pAttrib->GetEnd() == nIndex ) // start must be before
194 : {
195 : // Only expand if no feature and not in Exclude list!
196 : // Otherwise e.g. an UL would go until the new ULDB, thus expand both.
197 0 : if ( !maCharAttribs.FindEmptyAttrib( pAttrib->Which(), nIndex ) )
198 : {
199 0 : pAttrib->Expand( nNew );
200 : }
201 : else
202 0 : bResort = true;
203 : }
204 : // 2: attribute starts before and reaches past the index
205 0 : else if ( ( pAttrib->GetStart() < nIndex ) && ( pAttrib->GetEnd() > nIndex ) )
206 : {
207 0 : pAttrib->Expand( nNew );
208 : }
209 : // 3: attribute starts at Index
210 0 : else if ( pAttrib->GetStart() == nIndex )
211 : {
212 0 : if ( nIndex == 0 )
213 : {
214 0 : pAttrib->Expand( nNew );
215 : }
216 : else
217 0 : pAttrib->MoveForward( nNew );
218 : }
219 : }
220 :
221 : DBG_ASSERT( pAttrib->GetStart() <= pAttrib->GetEnd(), "Expand: Attribut verdreht!" );
222 : DBG_ASSERT( ( pAttrib->GetEnd() <= maText.getLength() ), "Expand: Attrib groesser als Absatz!" );
223 : DBG_ASSERT( !pAttrib->IsEmpty(), "Leeres Attribut nach ExpandAttribs?" );
224 : }
225 :
226 42 : if ( bResort )
227 0 : maCharAttribs.ResortAttribs();
228 : }
229 :
230 0 : void TextNode::CollapsAttribs( sal_uInt16 nIndex, sal_uInt16 nDeleted )
231 : {
232 0 : if ( !nDeleted )
233 0 : return;
234 :
235 0 : bool bResort = false;
236 0 : sal_uInt16 nEndChanges = nIndex+nDeleted;
237 :
238 0 : for ( sal_uInt16 nAttr = 0; nAttr < maCharAttribs.Count(); nAttr++ )
239 : {
240 0 : TextCharAttrib* pAttrib = maCharAttribs.GetAttrib( nAttr );
241 0 : bool bDelAttr = false;
242 0 : if ( pAttrib->GetEnd() >= nIndex )
243 : {
244 : // move all attributes that are behind the cursor
245 0 : if ( pAttrib->GetStart() >= nEndChanges )
246 : {
247 0 : pAttrib->MoveBackward( nDeleted );
248 : }
249 : // 1. delete inner attributes
250 0 : else if ( ( pAttrib->GetStart() >= nIndex ) && ( pAttrib->GetEnd() <= nEndChanges ) )
251 : {
252 : // special case: attribute covers the region exactly
253 : // => keep as an empty attribute
254 0 : if ( ( pAttrib->GetStart() == nIndex ) && ( pAttrib->GetEnd() == nEndChanges ) )
255 0 : pAttrib->GetEnd() = nIndex; // empty
256 : else
257 0 : bDelAttr = true;
258 : }
259 : // 2. attribute starts before, ends inside or after
260 0 : else if ( ( pAttrib->GetStart() <= nIndex ) && ( pAttrib->GetEnd() > nIndex ) )
261 : {
262 0 : if ( pAttrib->GetEnd() <= nEndChanges ) // ends inside
263 0 : pAttrib->GetEnd() = nIndex;
264 : else
265 0 : pAttrib->Collaps( nDeleted ); // ends after
266 : }
267 : // 3. attribute starts inside, ends after
268 0 : else if ( ( pAttrib->GetStart() >= nIndex ) && ( pAttrib->GetEnd() > nEndChanges ) )
269 : {
270 : // features are not allowed to expand!
271 0 : pAttrib->GetStart() = nEndChanges;
272 0 : pAttrib->MoveBackward( nDeleted );
273 : }
274 : }
275 :
276 : DBG_ASSERT( pAttrib->GetStart() <= pAttrib->GetEnd(), "Collaps: Attribut verdreht!" );
277 : DBG_ASSERT( ( pAttrib->GetEnd() <= maText.getLength()) || bDelAttr, "Collaps: Attrib groesser als Absatz!" );
278 0 : if ( bDelAttr /* || pAttrib->IsEmpty() */ )
279 : {
280 0 : bResort = true;
281 0 : maCharAttribs.RemoveAttrib( nAttr );
282 0 : delete pAttrib;
283 0 : nAttr--;
284 : }
285 0 : else if ( pAttrib->IsEmpty() )
286 0 : maCharAttribs.HasEmptyAttribs() = true;
287 : }
288 :
289 0 : if ( bResort )
290 0 : maCharAttribs.ResortAttribs();
291 : }
292 :
293 42 : void TextNode::InsertText( sal_uInt16 nPos, const OUString& rText )
294 : {
295 42 : maText = maText.replaceAt( nPos, 0, rText );
296 42 : ExpandAttribs( nPos, rText.getLength() );
297 42 : }
298 :
299 0 : void TextNode::InsertText( sal_uInt16 nPos, sal_Unicode c )
300 : {
301 0 : maText = maText.replaceAt( nPos, 0, OUString(c) );
302 0 : ExpandAttribs( nPos, 1 );
303 0 : }
304 :
305 0 : void TextNode::RemoveText( sal_uInt16 nPos, sal_uInt16 nChars )
306 : {
307 0 : maText = maText.replaceAt( nPos, nChars, "" );
308 0 : CollapsAttribs( nPos, nChars );
309 0 : }
310 :
311 0 : TextNode* TextNode::Split( sal_uInt16 nPos, bool bKeepEndingAttribs )
312 : {
313 0 : OUString aNewText;
314 0 : if ( nPos < maText.getLength() )
315 : {
316 0 : aNewText = maText.copy( nPos );
317 0 : maText = maText.copy(0, nPos);
318 : }
319 0 : TextNode* pNew = new TextNode( aNewText );
320 :
321 0 : for ( sal_uInt16 nAttr = 0; nAttr < maCharAttribs.Count(); nAttr++ )
322 : {
323 0 : TextCharAttrib* pAttrib = maCharAttribs.GetAttrib( nAttr );
324 0 : if ( pAttrib->GetEnd() < nPos )
325 : {
326 : // no change
327 : ;
328 : }
329 0 : else if ( pAttrib->GetEnd() == nPos )
330 : {
331 : // must be copied as an empty attribute
332 : // !FindAttrib only sensible if traversing backwards through the list!
333 0 : if ( bKeepEndingAttribs && !pNew->maCharAttribs.FindAttrib( pAttrib->Which(), 0 ) )
334 : {
335 0 : TextCharAttrib* pNewAttrib = new TextCharAttrib( *pAttrib );
336 0 : pNewAttrib->GetStart() = 0;
337 0 : pNewAttrib->GetEnd() = 0;
338 0 : pNew->maCharAttribs.InsertAttrib( pNewAttrib );
339 : }
340 : }
341 0 : else if ( pAttrib->IsInside( nPos ) || ( !nPos && !pAttrib->GetStart() ) )
342 : {
343 : // If cutting at the very beginning, the attribute has to be
344 : // copied and changed
345 0 : TextCharAttrib* pNewAttrib = new TextCharAttrib( *pAttrib );
346 0 : pNewAttrib->GetStart() = 0;
347 0 : pNewAttrib->GetEnd() = pAttrib->GetEnd()-nPos;
348 0 : pNew->maCharAttribs.InsertAttrib( pNewAttrib );
349 : // trim
350 0 : pAttrib->GetEnd() = nPos;
351 : }
352 : else
353 : {
354 : DBG_ASSERT( pAttrib->GetStart() >= nPos, "Start < nPos!" );
355 : DBG_ASSERT( pAttrib->GetEnd() >= nPos, "End < nPos!" );
356 : // move all into the new node (this)
357 0 : maCharAttribs.RemoveAttrib( nAttr );
358 0 : pNew->maCharAttribs.InsertAttrib( pAttrib );
359 0 : pAttrib->GetStart() = pAttrib->GetStart() - nPos;
360 0 : pAttrib->GetEnd() = pAttrib->GetEnd() - nPos;
361 0 : nAttr--;
362 : }
363 : }
364 0 : return pNew;
365 : }
366 :
367 0 : void TextNode::Append( const TextNode& rNode )
368 : {
369 0 : sal_Int32 nOldLen = maText.getLength();
370 :
371 0 : maText += rNode.GetText();
372 :
373 0 : const sal_uInt16 nAttribs = rNode.GetCharAttribs().Count();
374 0 : for ( sal_uInt16 nAttr = 0; nAttr < nAttribs; nAttr++ )
375 : {
376 0 : const TextCharAttrib& rAttrib = rNode.GetCharAttrib( nAttr );
377 0 : bool bMelted = false;
378 0 : if ( rAttrib.GetStart() == 0 )
379 : {
380 : // potentially merge attributes
381 0 : sal_uInt16 nTmpAttribs = maCharAttribs.Count();
382 0 : for ( sal_uInt16 nTmpAttr = 0; nTmpAttr < nTmpAttribs; nTmpAttr++ )
383 : {
384 0 : TextCharAttrib* pTmpAttrib = maCharAttribs.GetAttrib( nTmpAttr );
385 :
386 0 : if ( pTmpAttrib->GetEnd() == nOldLen )
387 : {
388 0 : if ( ( pTmpAttrib->Which() == rAttrib.Which() ) &&
389 0 : ( pTmpAttrib->GetAttr() == rAttrib.GetAttr() ) )
390 : {
391 0 : pTmpAttrib->GetEnd() =
392 0 : pTmpAttrib->GetEnd() + rAttrib.GetLen();
393 0 : bMelted = true;
394 0 : break; // there can be only one of this type at this position
395 : }
396 : }
397 : }
398 : }
399 :
400 0 : if ( !bMelted )
401 : {
402 0 : TextCharAttrib* pNewAttrib = new TextCharAttrib( rAttrib );
403 0 : pNewAttrib->GetStart() = pNewAttrib->GetStart() + nOldLen;
404 0 : pNewAttrib->GetEnd() = pNewAttrib->GetEnd() + nOldLen;
405 0 : maCharAttribs.InsertAttrib( pNewAttrib );
406 : }
407 : }
408 0 : }
409 :
410 52 : TextDoc::TextDoc()
411 : {
412 52 : mnLeftMargin = 0;
413 52 : };
414 :
415 100 : TextDoc::~TextDoc()
416 : {
417 50 : DestroyTextNodes();
418 50 : }
419 :
420 129 : void TextDoc::Clear()
421 : {
422 129 : DestroyTextNodes();
423 129 : }
424 :
425 179 : void TextDoc::DestroyTextNodes()
426 : {
427 358 : for ( sal_uLong nNode = 0; nNode < maTextNodes.size(); nNode++ )
428 179 : delete maTextNodes[ nNode ];
429 179 : maTextNodes.clear();
430 179 : }
431 :
432 17 : OUString TextDoc::GetText( const sal_Unicode* pSep ) const
433 : {
434 17 : sal_uLong nNodes = maTextNodes.size();
435 :
436 17 : OUString aASCIIText;
437 17 : sal_uLong nLastNode = nNodes-1;
438 34 : for ( sal_uLong nNode = 0; nNode < nNodes; nNode++ )
439 : {
440 17 : TextNode* pNode = maTextNodes[ nNode ];
441 17 : OUString aTmp( pNode->GetText() );
442 17 : aASCIIText += aTmp;
443 17 : if ( pSep && ( nNode != nLastNode ) )
444 0 : aASCIIText += pSep;
445 17 : }
446 :
447 17 : return aASCIIText;
448 : }
449 :
450 0 : OUString TextDoc::GetText( sal_uLong nPara ) const
451 : {
452 0 : OUString aText;
453 :
454 0 : TextNode* pNode = ( nPara < maTextNodes.size() ) ? maTextNodes[ nPara ] : 0;
455 0 : if ( pNode )
456 0 : aText = pNode->GetText();
457 :
458 0 : return aText;
459 : }
460 :
461 0 : sal_uLong TextDoc::GetTextLen( const sal_Unicode* pSep, const TextSelection* pSel ) const
462 : {
463 0 : sal_uLong nLen = 0;
464 0 : sal_uLong nNodes = maTextNodes.size();
465 0 : if ( nNodes )
466 : {
467 0 : sal_uLong nStartNode = 0;
468 0 : sal_uLong nEndNode = nNodes-1;
469 0 : if ( pSel )
470 : {
471 0 : nStartNode = pSel->GetStart().GetPara();
472 0 : nEndNode = pSel->GetEnd().GetPara();
473 : }
474 :
475 0 : for ( sal_uLong nNode = nStartNode; nNode <= nEndNode; nNode++ )
476 : {
477 0 : TextNode* pNode = maTextNodes[ nNode ];
478 :
479 0 : sal_uInt16 nS = 0;
480 0 : sal_Int32 nE = pNode->GetText().getLength();
481 0 : if ( pSel && ( nNode == pSel->GetStart().GetPara() ) )
482 0 : nS = pSel->GetStart().GetIndex();
483 0 : if ( pSel && ( nNode == pSel->GetEnd().GetPara() ) )
484 0 : nE = pSel->GetEnd().GetIndex();
485 :
486 0 : nLen += ( nE - nS );
487 : }
488 :
489 0 : if ( pSep )
490 0 : nLen += (nEndNode-nStartNode) * rtl_ustr_getLength(pSep);
491 : }
492 :
493 0 : return nLen;
494 : }
495 :
496 0 : TextPaM TextDoc::InsertText( const TextPaM& rPaM, sal_Unicode c )
497 : {
498 : DBG_ASSERT( c != 0x0A, "TextDoc::InsertText: Zeilentrenner in Absatz nicht erlaubt!" );
499 : DBG_ASSERT( c != 0x0D, "TextDoc::InsertText: Zeilentrenner in Absatz nicht erlaubt!" );
500 :
501 0 : TextNode* pNode = maTextNodes[ rPaM.GetPara() ];
502 0 : pNode->InsertText( rPaM.GetIndex(), c );
503 :
504 0 : TextPaM aPaM( rPaM.GetPara(), rPaM.GetIndex()+1 );
505 0 : return aPaM;
506 : }
507 :
508 42 : TextPaM TextDoc::InsertText( const TextPaM& rPaM, const OUString& rStr )
509 : {
510 : DBG_ASSERT( rStr.indexOf( 0x0A ) == -1, "TextDoc::InsertText: Zeilentrenner in Absatz nicht erlaubt!" );
511 : DBG_ASSERT( rStr.indexOf( 0x0D ) == -1, "TextDoc::InsertText: Zeilentrenner in Absatz nicht erlaubt!" );
512 :
513 42 : TextNode* pNode = maTextNodes[ rPaM.GetPara() ];
514 42 : pNode->InsertText( rPaM.GetIndex(), rStr );
515 :
516 42 : TextPaM aPaM( rPaM.GetPara(), rPaM.GetIndex()+rStr.getLength() );
517 42 : return aPaM;
518 : }
519 :
520 0 : TextPaM TextDoc::InsertParaBreak( const TextPaM& rPaM, bool bKeepEndingAttribs )
521 : {
522 0 : TextNode* pNode = maTextNodes[ rPaM.GetPara() ];
523 0 : TextNode* pNew = pNode->Split( rPaM.GetIndex(), bKeepEndingAttribs );
524 :
525 0 : maTextNodes.insert( maTextNodes.begin() + rPaM.GetPara() + 1, pNew );
526 :
527 0 : TextPaM aPaM( rPaM.GetPara()+1, 0 );
528 0 : return aPaM;
529 : }
530 :
531 0 : TextPaM TextDoc::ConnectParagraphs( TextNode* pLeft, TextNode* pRight )
532 : {
533 0 : sal_Int32 nPrevLen = pLeft->GetText().getLength();
534 0 : pLeft->Append( *pRight );
535 :
536 : // the paragraph on the right vanishes
537 0 : maTextNodes.erase( std::find( maTextNodes.begin(), maTextNodes.end(), pRight ) );
538 0 : delete pRight;
539 :
540 0 : sal_uLong nLeft = ::std::find( maTextNodes.begin(), maTextNodes.end(), pLeft ) - maTextNodes.begin();
541 0 : TextPaM aPaM( nLeft, nPrevLen );
542 0 : return aPaM;
543 : }
544 :
545 0 : TextPaM TextDoc::RemoveChars( const TextPaM& rPaM, sal_uInt16 nChars )
546 : {
547 0 : TextNode* pNode = maTextNodes[ rPaM.GetPara() ];
548 0 : pNode->RemoveText( rPaM.GetIndex(), nChars );
549 :
550 0 : return rPaM;
551 : }
552 :
553 0 : bool TextDoc::IsValidPaM( const TextPaM& rPaM )
554 : {
555 0 : if ( rPaM.GetPara() >= maTextNodes.size() )
556 : {
557 : OSL_FAIL( "PaM: Para out of range" );
558 0 : return false;
559 : }
560 0 : TextNode * pNode = maTextNodes[ rPaM.GetPara() ];
561 0 : if ( rPaM.GetIndex() > pNode->GetText().getLength() )
562 : {
563 : OSL_FAIL( "PaM: Index out of range" );
564 0 : return false;
565 : }
566 0 : return true;
567 : }
568 :
569 : /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
|