1 /************************************************************************* 2 * 3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 4 * 5 * Copyright 2000, 2010 Oracle and/or its affiliates. 6 * 7 * OpenOffice.org - a multi-platform office productivity suite 8 * 9 * This file is part of OpenOffice.org. 10 * 11 * OpenOffice.org is free software: you can redistribute it and/or modify 12 * it under the terms of the GNU Lesser General Public License version 3 13 * only, as published by the Free Software Foundation. 14 * 15 * OpenOffice.org is distributed in the hope that it will be useful, 16 * but WITHOUT ANY WARRANTY; without even the implied warranty of 17 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 18 * GNU Lesser General Public License version 3 for more details 19 * (a copy is included in the LICENSE file that accompanied this code). 20 * 21 * You should have received a copy of the GNU Lesser General Public License 22 * version 3 along with OpenOffice.org. If not, see 23 * <http://www.openoffice.org/license.html> 24 * for a copy of the LGPLv3 License. 25 * 26 ************************************************************************/ 27 28 // MARKER(update_precomp.py): autogen include statement, do not remove 29 #include "precompiled_sw.hxx" 30 #include "accportions.hxx" 31 #include <tools/debug.hxx> 32 #include <rtl/ustring.hxx> 33 #include <com/sun/star/i18n/Boundary.hpp> 34 #include <txttypes.hxx> 35 36 // for portion replacement in Special() 37 #ifndef _ACCESS_HRC 38 #include "access.hrc" 39 #endif 40 #include <tools/resid.hxx> 41 #include "viewopt.hxx" 42 43 // for GetWordBoundary(...), GetSentenceBoundary(...): 44 #include <breakit.hxx> 45 #include <com/sun/star/i18n/WordType.hpp> 46 #include <com/sun/star/i18n/XBreakIterator.hpp> 47 #include <ndtxt.hxx> 48 49 // for FillSpecialPos(...) 50 #include "crstate.hxx" 51 52 // for SwAccessibleContext::GetResource() 53 #include "acccontext.hxx" 54 55 // for Post-It replacement text: 56 #include "txatbase.hxx" 57 #include "fmtfld.hxx" 58 #include "fldbas.hxx" 59 #include "docufld.hxx" 60 61 // for in-line graphics replacement: 62 #include "ndindex.hxx" 63 #include "ndnotxt.hxx" 64 #include "fmtflcnt.hxx" 65 #include "frmfmt.hxx" 66 #include "fmtcntnt.hxx" 67 68 69 using namespace ::com::sun::star; 70 71 using rtl::OUString; 72 using rtl::OUStringBuffer; 73 using i18n::Boundary; 74 75 76 // 'portion type' for terminating portions 77 #define POR_TERMINATE 0 78 79 80 // portion attributes 81 #define PORATTR_SPECIAL 1 82 #define PORATTR_READONLY 2 83 #define PORATTR_GRAY 4 84 #define PORATTR_TERM 128 85 86 SwAccessiblePortionData::SwAccessiblePortionData( 87 const SwTxtNode* pTxtNd, 88 const SwViewOption* pViewOpt ) : 89 SwPortionHandler(), 90 pTxtNode( pTxtNd ), 91 aBuffer(), 92 nModelPosition( 0 ), 93 bFinished( sal_False ), 94 pViewOptions( pViewOpt ), 95 sAccessibleString(), 96 aLineBreaks(), 97 aModelPositions(), 98 aAccessiblePositions(), 99 pSentences( 0 ), 100 nBeforePortions( 0 ), 101 bLastIsSpecial( sal_False ) 102 { 103 DBG_ASSERT( pTxtNode != NULL, "Text node is needed!" ); 104 105 // reserve some space to reduce memory allocations 106 aLineBreaks.reserve( 5 ); 107 aModelPositions.reserve( 10 ); 108 aAccessiblePositions.reserve( 10 ); 109 110 // always include 'first' line-break position 111 aLineBreaks.push_back( 0 ); 112 } 113 114 SwAccessiblePortionData::~SwAccessiblePortionData() 115 { 116 delete pSentences; 117 } 118 119 void SwAccessiblePortionData::Text(sal_uInt16 nLength, sal_uInt16 nType) 120 { 121 DBG_ASSERT( (nModelPosition + nLength) <= pTxtNode->GetTxt().Len(), 122 "portion exceeds model string!" ); 123 124 DBG_ASSERT( !bFinished, "We are already done!" ); 125 126 // ignore zero-length portions 127 if( nLength == 0 ) 128 return; 129 130 // store 'old' positions 131 aModelPositions.push_back( nModelPosition ); 132 aAccessiblePositions.push_back( aBuffer.getLength() ); 133 134 // store portion attributes 135 sal_uInt8 nAttr = IsGrayPortionType(nType) ? PORATTR_GRAY : 0; 136 aPortionAttrs.push_back( nAttr ); 137 138 // update buffer + nModelPosition 139 aBuffer.append( OUString( 140 pTxtNode->GetTxt().Copy( 141 static_cast<sal_uInt16>( nModelPosition ), 142 nLength ) ) ); 143 nModelPosition += nLength; 144 145 bLastIsSpecial = sal_False; 146 } 147 148 void SwAccessiblePortionData::Special( 149 sal_uInt16 nLength, const String& rText, sal_uInt16 nType) 150 { 151 DBG_ASSERT( nModelPosition >= 0, "illegal position" ); 152 DBG_ASSERT( (nModelPosition + nLength) <= pTxtNode->GetTxt().Len(), 153 "portion exceeds model string!" ); 154 155 DBG_ASSERT( !bFinished, "We are already done!" ); 156 157 // construct string with representation; either directly from 158 // rText, or use resources for special case portions 159 String sDisplay; 160 switch( nType ) 161 { 162 case POR_POSTITS: 163 case POR_FLYCNT: 164 case POR_GRFNUM: 165 sDisplay = String(sal_Unicode(0xfffc)); 166 167 break; 168 case POR_NUMBER: 169 { 170 OUStringBuffer aTmpBuffer( rText.Len() + 1 ); 171 aTmpBuffer.append( rText ); 172 aTmpBuffer.append( sal_Unicode(' ') ); 173 sDisplay = aTmpBuffer.makeStringAndClear(); 174 break; 175 } 176 // --> OD 2010-06-04 #i111768# - apply patch from kstribley: 177 // Include the control characters. 178 case POR_CONTROLCHAR: 179 { 180 OUStringBuffer aTmpBuffer( rText.Len() + 1 ); 181 aTmpBuffer.append( rText ); 182 aTmpBuffer.append( pTxtNode->GetTxt().GetChar(nModelPosition) ); 183 sDisplay = aTmpBuffer.makeStringAndClear(); 184 break; 185 } 186 // <-- 187 default: 188 sDisplay = rText; 189 break; 190 } 191 192 // ignore zero/zero portions (except for terminators) 193 if( (nLength == 0) && (sDisplay.Len() == 0) && (nType != POR_TERMINATE) ) 194 return; 195 196 // special treatment for zero length portion at the beginning: 197 // count as 'before' portion 198 if( ( nLength == 0 ) && ( nModelPosition == 0 ) ) 199 nBeforePortions++; 200 201 // store the 'old' positions 202 aModelPositions.push_back( nModelPosition ); 203 aAccessiblePositions.push_back( aBuffer.getLength() ); 204 205 // store portion attributes 206 sal_uInt8 nAttr = PORATTR_SPECIAL; 207 if( IsGrayPortionType(nType) ) nAttr |= PORATTR_GRAY; 208 if( nLength == 0 ) nAttr |= PORATTR_READONLY; 209 if( nType == POR_TERMINATE ) nAttr |= PORATTR_TERM; 210 aPortionAttrs.push_back( nAttr ); 211 212 // update buffer + nModelPosition 213 aBuffer.append( OUString(sDisplay) ); 214 nModelPosition += nLength; 215 216 // remember 'last' special portion (unless it's our own 'closing' 217 // portions from 'Finish()' 218 if( nType != POR_TERMINATE ) 219 bLastIsSpecial = sal_True; 220 } 221 222 void SwAccessiblePortionData::LineBreak() 223 { 224 DBG_ASSERT( !bFinished, "We are already done!" ); 225 226 aLineBreaks.push_back( aBuffer.getLength() ); 227 } 228 229 void SwAccessiblePortionData::Skip(sal_uInt16 nLength) 230 { 231 DBG_ASSERT( !bFinished, "We are already done!" ); 232 DBG_ASSERT( aModelPositions.size() == 0, "Never Skip() after portions" ); 233 DBG_ASSERT( nLength <= pTxtNode->GetTxt().Len(), "skip exceeds model string!" ); 234 235 nModelPosition += nLength; 236 } 237 238 void SwAccessiblePortionData::Finish() 239 { 240 DBG_ASSERT( !bFinished, "We are already done!" ); 241 242 // include terminator values: always include two 'last character' 243 // markers in the position arrays to make sure we always find one 244 // position before the end 245 Special( 0, String(), POR_TERMINATE ); 246 Special( 0, String(), POR_TERMINATE ); 247 LineBreak(); 248 LineBreak(); 249 250 sAccessibleString = aBuffer.makeStringAndClear(); 251 bFinished = sal_True; 252 } 253 254 255 sal_Bool SwAccessiblePortionData::IsPortionAttrSet( 256 size_t nPortionNo, sal_uInt8 nAttr ) const 257 { 258 DBG_ASSERT( nPortionNo < aPortionAttrs.size(), 259 "Illegal portion number" ); 260 return (aPortionAttrs[nPortionNo] & nAttr) != 0; 261 } 262 263 sal_Bool SwAccessiblePortionData::IsSpecialPortion( size_t nPortionNo ) const 264 { 265 return IsPortionAttrSet(nPortionNo, PORATTR_SPECIAL); 266 } 267 268 sal_Bool SwAccessiblePortionData::IsReadOnlyPortion( size_t nPortionNo ) const 269 { 270 return IsPortionAttrSet(nPortionNo, PORATTR_READONLY); 271 } 272 273 sal_Bool SwAccessiblePortionData::IsGrayPortionType( sal_uInt16 nType ) const 274 { 275 // gray portions? 276 // Compare with: inftxt.cxx, SwTxtPaintInfo::DrawViewOpt(...) 277 sal_Bool bGray = sal_False; 278 switch( nType ) 279 { 280 case POR_FTN: 281 case POR_ISOREF: 282 case POR_REF: 283 case POR_QUOVADIS: 284 case POR_NUMBER: 285 case POR_FLD: 286 case POR_URL: 287 case POR_ISOTOX: 288 case POR_TOX: 289 case POR_HIDDEN: 290 bGray = !pViewOptions->IsPagePreview() && 291 !pViewOptions->IsReadonly() && SwViewOption::IsFieldShadings(); 292 break; 293 case POR_TAB: bGray = pViewOptions->IsTab(); break; 294 case POR_SOFTHYPH: bGray = pViewOptions->IsSoftHyph(); break; 295 case POR_BLANK: bGray = pViewOptions->IsHardBlank(); break; 296 default: 297 break; // bGray is false 298 } 299 return bGray; 300 } 301 302 303 const OUString& SwAccessiblePortionData::GetAccessibleString() const 304 { 305 DBG_ASSERT( bFinished, "Shouldn't call this before we are done!" ); 306 307 return sAccessibleString; 308 } 309 310 311 void SwAccessiblePortionData::GetLineBoundary( 312 Boundary& rBound, 313 sal_Int32 nPos ) const 314 { 315 FillBoundary( rBound, aLineBreaks, 316 FindBreak( aLineBreaks, nPos ) ); 317 } 318 319 // --> OD 2008-05-30 #i89175# 320 sal_Int32 SwAccessiblePortionData::GetLineCount() const 321 { 322 size_t nBreaks = aLineBreaks.size(); 323 // A non-empty paragraph has at least 4 breaks: one for each line3 and 324 // 3 additional ones. 325 // An empty paragraph has 3 breaks. 326 // Less than 3 breaks is an error case. 327 sal_Int32 nLineCount = ( nBreaks > 3 ) 328 ? nBreaks - 3 329 : ( ( nBreaks == 3 ) ? 1 : 0 ); 330 return nLineCount; 331 } 332 333 sal_Int32 SwAccessiblePortionData::GetLineNo( const sal_Int32 nPos ) const 334 { 335 sal_Int32 nLineNo = FindBreak( aLineBreaks, nPos ); 336 337 // handling of position after last character 338 const sal_Int32 nLineCount( GetLineCount() ); 339 if ( nLineNo >= nLineCount ) 340 { 341 nLineNo = nLineCount - 1; 342 } 343 344 return nLineNo; 345 } 346 347 void SwAccessiblePortionData::GetBoundaryOfLine( const sal_Int32 nLineNo, 348 i18n::Boundary& rLineBound ) 349 { 350 FillBoundary( rLineBound, aLineBreaks, nLineNo ); 351 } 352 // <-- 353 354 void SwAccessiblePortionData::GetLastLineBoundary( 355 Boundary& rBound ) const 356 { 357 DBG_ASSERT( aLineBreaks.size() >= 2, "need min + max value" ); 358 359 // The last two positions except the two deleimiters are the ones 360 // we are looking for, except for empty paragraphs (nBreaks==3) 361 size_t nBreaks = aLineBreaks.size(); 362 FillBoundary( rBound, aLineBreaks, nBreaks <= 3 ? 0 : nBreaks-4 ); 363 } 364 365 sal_uInt16 SwAccessiblePortionData::GetModelPosition( sal_Int32 nPos ) const 366 { 367 DBG_ASSERT( nPos >= 0, "illegal position" ); 368 DBG_ASSERT( nPos <= sAccessibleString.getLength(), "illegal position" ); 369 370 // find the portion number 371 size_t nPortionNo = FindBreak( aAccessiblePositions, nPos ); 372 373 // get model portion size 374 sal_Int32 nStartPos = aModelPositions[nPortionNo]; 375 376 // if it's a non-special portion, move into the portion, else 377 // return the portion start 378 if( ! IsSpecialPortion( nPortionNo ) ) 379 { 380 // 'wide' portions have to be of the same width 381 DBG_ASSERT( ( aModelPositions[nPortionNo+1] - nStartPos ) == 382 ( aAccessiblePositions[nPortionNo+1] - 383 aAccessiblePositions[nPortionNo] ), 384 "accesability portion disagrees with text model" ); 385 386 sal_Int32 nWithinPortion = nPos - aAccessiblePositions[nPortionNo]; 387 nStartPos += nWithinPortion; 388 } 389 // else: return nStartPos unmodified 390 391 DBG_ASSERT( (nStartPos >= 0) && (nStartPos < USHRT_MAX), 392 "How can the SwTxtNode have so many characters?" ); 393 return static_cast<sal_uInt16>(nStartPos); 394 } 395 396 void SwAccessiblePortionData::FillBoundary( 397 Boundary& rBound, 398 const Positions_t& rPositions, 399 size_t nPos ) const 400 { 401 rBound.startPos = rPositions[nPos]; 402 rBound.endPos = rPositions[nPos+1]; 403 } 404 405 406 size_t SwAccessiblePortionData::FindBreak( 407 const Positions_t& rPositions, 408 sal_Int32 nValue ) const 409 { 410 DBG_ASSERT( rPositions.size() >= 2, "need min + max value" ); 411 DBG_ASSERT( rPositions[0] <= nValue, "need min value" ); 412 DBG_ASSERT( rPositions[rPositions.size()-1] >= nValue, 413 "need first terminator value" ); 414 DBG_ASSERT( rPositions[rPositions.size()-2] >= nValue, 415 "need second terminator value" ); 416 417 size_t nMin = 0; 418 size_t nMax = rPositions.size()-2; 419 420 // loop until no more than two candidates are left 421 while( nMin+1 < nMax ) 422 { 423 // check loop invariants 424 DBG_ASSERT( ( (nMin == 0) && (rPositions[nMin] <= nValue) ) || 425 ( (nMin != 0) && (rPositions[nMin] < nValue) ), 426 "minvalue not minimal" ); 427 DBG_ASSERT( nValue <= rPositions[nMax], "max value not maximal" ); 428 429 // get middle (and ensure progress) 430 size_t nMiddle = (nMin + nMax)/2; 431 DBG_ASSERT( nMin < nMiddle, "progress?" ); 432 DBG_ASSERT( nMiddle < nMax, "progress?" ); 433 434 // check array 435 DBG_ASSERT( rPositions[nMin] <= rPositions[nMiddle], 436 "garbled positions array" ); 437 DBG_ASSERT( rPositions[nMiddle] <= rPositions[nMax], 438 "garbled positions array" ); 439 440 if( nValue > rPositions[nMiddle] ) 441 nMin = nMiddle; 442 else 443 nMax = nMiddle; 444 } 445 446 // only two are left; we only need to check which one is the winner 447 DBG_ASSERT( (nMax == nMin) || (nMax == nMin+1), "only two left" ); 448 if( (rPositions[nMin] < nValue) && (rPositions[nMin+1] <= nValue) ) 449 nMin = nMin+1; 450 451 // finally, check to see whether the returned value is the 'right' position 452 DBG_ASSERT( rPositions[nMin] <= nValue, "not smaller or equal" ); 453 DBG_ASSERT( nValue <= rPositions[nMin+1], "not equal or larger" ); 454 DBG_ASSERT( (nMin == 0) || (rPositions[nMin-1] <= nValue), 455 "earlier value should have been returned" ); 456 457 DBG_ASSERT( nMin < rPositions.size()-1, 458 "shouldn't return last position (due to termintator values)" ); 459 460 return nMin; 461 } 462 463 size_t SwAccessiblePortionData::FindLastBreak( 464 const Positions_t& rPositions, 465 sal_Int32 nValue ) const 466 { 467 size_t nResult = FindBreak( rPositions, nValue ); 468 469 // skip 'zero-length' portions 470 // --> OD 2006-10-19 #i70538# 471 // consider size of <rPosition> and ignore last entry 472 // while( rPositions[nResult+1] <= nValue ) 473 while ( nResult < rPositions.size() - 2 && 474 rPositions[nResult+1] <= nValue ) 475 { 476 nResult++; 477 } 478 // <-- 479 480 return nResult; 481 } 482 483 484 void SwAccessiblePortionData::GetSentenceBoundary( 485 Boundary& rBound, 486 sal_Int32 nPos ) 487 { 488 DBG_ASSERT( nPos >= 0, "illegal position; check before" ); 489 DBG_ASSERT( nPos < sAccessibleString.getLength(), "illegal position" ); 490 491 if( pSentences == NULL ) 492 { 493 DBG_ASSERT( pBreakIt != NULL, "We always need a break." ); 494 DBG_ASSERT( pBreakIt->GetBreakIter().is(), "No break-iterator." ); 495 if( pBreakIt->GetBreakIter().is() ) 496 { 497 pSentences = new Positions_t(); 498 pSentences->reserve(10); 499 500 // use xBreak->endOfSentence to iterate over all words; store 501 // positions in pSentences 502 sal_Int32 nCurrent = 0; 503 sal_Int32 nLength = sAccessibleString.getLength(); 504 do 505 { 506 pSentences->push_back( nCurrent ); 507 508 sal_uInt16 nModelPos = GetModelPosition( nCurrent ); 509 510 sal_Int32 nNew = pBreakIt->GetBreakIter()->endOfSentence( 511 sAccessibleString, nCurrent, 512 pBreakIt->GetLocale(pTxtNode->GetLang(nModelPos)) ) + 1; 513 514 if( (nNew < 0) && (nNew > nLength) ) 515 nNew = nLength; 516 else if (nNew <= nCurrent) 517 nNew = nCurrent + 1; // ensure forward progress 518 519 nCurrent = nNew; 520 } 521 while (nCurrent < nLength); 522 523 // finish with two terminators 524 pSentences->push_back( nLength ); 525 pSentences->push_back( nLength ); 526 } 527 else 528 { 529 // no break iterator -> empty word 530 rBound.startPos = 0; 531 rBound.endPos = 0; 532 return; 533 } 534 } 535 536 FillBoundary( rBound, *pSentences, FindBreak( *pSentences, nPos ) ); 537 } 538 539 void SwAccessiblePortionData::GetAttributeBoundary( 540 Boundary& rBound, 541 sal_Int32 nPos) const 542 { 543 DBG_ASSERT( pTxtNode != NULL, "Need SwTxtNode!" ); 544 545 // attribute boundaries can only occur on portion boundaries 546 FillBoundary( rBound, aAccessiblePositions, 547 FindBreak( aAccessiblePositions, nPos ) ); 548 } 549 550 551 sal_Int32 SwAccessiblePortionData::GetAccessiblePosition( sal_uInt16 nPos ) const 552 { 553 DBG_ASSERT( nPos <= pTxtNode->GetTxt().Len(), "illegal position" ); 554 555 // find the portion number 556 // --> OD 2006-10-19 #i70538# 557 // consider "empty" model portions - e.g. number portion 558 size_t nPortionNo = FindLastBreak( aModelPositions, 559 static_cast<sal_Int32>(nPos) ); 560 // <-- 561 562 sal_Int32 nRet = aAccessiblePositions[nPortionNo]; 563 564 // if the model portion has more than one position, go into it; 565 // else return that position 566 sal_Int32 nStartPos = aModelPositions[nPortionNo]; 567 sal_Int32 nEndPos = aModelPositions[nPortionNo+1]; 568 if( (nEndPos - nStartPos) > 1 ) 569 { 570 // 'wide' portions have to be of the same width 571 DBG_ASSERT( ( nEndPos - nStartPos ) == 572 ( aAccessiblePositions[nPortionNo+1] - 573 aAccessiblePositions[nPortionNo] ), 574 "accesability portion disagrees with text model" ); 575 576 sal_Int32 nWithinPortion = nPos - aModelPositions[nPortionNo]; 577 nRet += nWithinPortion; 578 } 579 // else: return nRet unmodified 580 581 DBG_ASSERT( (nRet >= 0) && (nRet <= sAccessibleString.getLength()), 582 "too long!" ); 583 return nRet; 584 } 585 586 sal_uInt16 SwAccessiblePortionData::FillSpecialPos( 587 sal_Int32 nPos, 588 SwSpecialPos& rPos, 589 SwSpecialPos*& rpPos ) const 590 { 591 size_t nPortionNo = FindLastBreak( aAccessiblePositions, nPos ); 592 593 sal_uInt8 nExtend(SP_EXTEND_RANGE_NONE); 594 sal_Int32 nRefPos(0); 595 sal_Int32 nModelPos(0); 596 597 if( nPortionNo < nBeforePortions ) 598 { 599 nExtend = SP_EXTEND_RANGE_BEFORE; 600 rpPos = &rPos; 601 } 602 else 603 { 604 sal_Int32 nModelEndPos = aModelPositions[nPortionNo+1]; 605 nModelPos = aModelPositions[nPortionNo]; 606 607 // skip backwards over zero-length portions, since GetCharRect() 608 // counts all model-zero-length portions as belonging to the 609 // previus portion 610 size_t nCorePortionNo = nPortionNo; 611 while( nModelPos == nModelEndPos ) 612 { 613 nCorePortionNo--; 614 nModelEndPos = nModelPos; 615 nModelPos = aModelPositions[nCorePortionNo]; 616 617 DBG_ASSERT( nModelPos >= 0, "Can't happen." ); 618 DBG_ASSERT( nCorePortionNo >= nBeforePortions, "Can't happen." ); 619 } 620 DBG_ASSERT( nModelPos != nModelEndPos, 621 "portion with core-representation expected" ); 622 623 // if we have anything except plain text, compute nExtend + nRefPos 624 if( (nModelEndPos - nModelPos == 1) && 625 (pTxtNode->GetTxt().GetChar(static_cast<sal_uInt16>(nModelPos)) != 626 sAccessibleString.getStr()[nPos]) ) 627 { 628 // case 1: a one-character, non-text portion 629 // reference position is the first accessibilty for our 630 // core portion 631 nRefPos = aAccessiblePositions[ nCorePortionNo ]; 632 nExtend = SP_EXTEND_RANGE_NONE; 633 rpPos = &rPos; 634 } 635 else if(nPortionNo != nCorePortionNo) 636 { 637 // case 2: a multi-character (text!) portion, followed by 638 // zero-length portions 639 // reference position is the first character of the next 640 // portion, and we are 'behind' 641 nRefPos = aAccessiblePositions[ nCorePortionNo+1 ]; 642 nExtend = SP_EXTEND_RANGE_BEHIND; 643 rpPos = &rPos; 644 } 645 else 646 { 647 // case 3: regular text portion 648 DBG_ASSERT( ( nModelEndPos - nModelPos ) == 649 ( aAccessiblePositions[nPortionNo+1] - 650 aAccessiblePositions[nPortionNo] ), 651 "text portion expected" ); 652 653 nModelPos += nPos - aAccessiblePositions[ nPortionNo ]; 654 rpPos = NULL; 655 } 656 } 657 if( rpPos != NULL ) 658 { 659 DBG_ASSERT( rpPos == &rPos, "Yes!" ); 660 DBG_ASSERT( nRefPos <= nPos, "wrong reference" ); 661 DBG_ASSERT( (nExtend == SP_EXTEND_RANGE_NONE) || 662 (nExtend == SP_EXTEND_RANGE_BEFORE) || 663 (nExtend == SP_EXTEND_RANGE_BEHIND), "need extend" ); 664 665 // get the line number, and adjust nRefPos for the line 666 // (if necessary) 667 size_t nRefLine = FindBreak( aLineBreaks, nRefPos ); 668 size_t nMyLine = FindBreak( aLineBreaks, nPos ); 669 sal_uInt16 nLineOffset = static_cast<sal_uInt16>( nMyLine - nRefLine ); 670 if( nLineOffset != 0 ) 671 nRefPos = aLineBreaks[ nMyLine ]; 672 673 // fill char offset and 'special position' 674 rPos.nCharOfst = static_cast<sal_uInt16>( nPos - nRefPos ); 675 rPos.nExtendRange = nExtend; 676 rPos.nLineOfst = nLineOffset; 677 } 678 679 return static_cast<sal_uInt16>( nModelPos ); 680 } 681 682 void SwAccessiblePortionData::AdjustAndCheck( 683 sal_Int32 nPos, 684 size_t& nPortionNo, 685 sal_uInt16& nCorePos, 686 sal_Bool& bEdit) const 687 { 688 // find portion and get mode position 689 nPortionNo = FindBreak( aAccessiblePositions, nPos ); 690 nCorePos = static_cast<sal_uInt16>( aModelPositions[ nPortionNo ] ); 691 692 // for special portions, make sure we're on a portion boundary 693 // for text portions, add the in-portion offset 694 if( IsSpecialPortion( nPortionNo ) ) 695 bEdit &= nPos == aAccessiblePositions[nPortionNo]; 696 else 697 nCorePos = static_cast<sal_uInt16>( nCorePos + 698 nPos - aAccessiblePositions[nPortionNo] ); 699 } 700 701 sal_Bool SwAccessiblePortionData::GetEditableRange( 702 sal_Int32 nStart, sal_Int32 nEnd, 703 sal_uInt16& nCoreStart, sal_uInt16& nCoreEnd ) const 704 { 705 sal_Bool bIsEditable = sal_True; 706 707 // get start and end portions 708 size_t nStartPortion, nEndPortion; 709 AdjustAndCheck( nStart, nStartPortion, nCoreStart, bIsEditable ); 710 AdjustAndCheck( nEnd, nEndPortion, nCoreEnd, bIsEditable ); 711 712 // iterate over portions, and make sure there is no read-only portion 713 // in-between 714 size_t nLastPortion = nEndPortion; 715 716 // don't count last portion if we're in front of a special portion 717 if( IsSpecialPortion(nLastPortion) ) 718 { 719 if (nLastPortion > 0) 720 nLastPortion--; 721 else 722 // special case: because size_t is usually unsigned, we can't just 723 // decrease nLastPortion to -1 (which would normally do the job, so 724 // this whole if wouldn't be needed). Instead, we'll do this 725 // special case and just increae the start portion beyond the last 726 // portion to make sure the loop below will have zero iteration. 727 nStartPortion = nLastPortion + 1; 728 } 729 730 for( size_t nPor = nStartPortion; nPor <= nLastPortion; nPor ++ ) 731 { 732 bIsEditable &= ! IsReadOnlyPortion( nPor ); 733 } 734 735 return bIsEditable; 736 } 737 738 sal_Bool SwAccessiblePortionData::IsValidCorePosition( sal_uInt16 nPos ) const 739 { 740 // a position is valid its within the model positions that we know 741 return ( aModelPositions[0] <= nPos ) && 742 ( nPos <= aModelPositions[ aModelPositions.size()-1 ] ); 743 } 744 745 sal_uInt16 SwAccessiblePortionData::GetFirstValidCorePosition() const 746 { 747 return static_cast<sal_uInt16>( aModelPositions[0] ); 748 } 749 750 sal_uInt16 SwAccessiblePortionData::GetLastValidCorePosition() const 751 { 752 return static_cast<sal_uInt16>( aModelPositions[ aModelPositions.size()-1 ] ); 753 } 754