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