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