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 //IAccessibility2 Implementation 2009----- 68 //#include "accnote.hxx" 69 //-----IAccessibility2 Implementation 2009 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 //IAccessibility2 Implementation 2009----- 148 void SwAccessiblePortionData::SetAttrFieldType( sal_uInt16 nAttrFldType ) 149 { 150 aAttrFieldType.push_back(nAttrFldType); 151 return; 152 } 153 //-----IAccessibility2 Implementation 2009 154 155 void SwAccessiblePortionData::Special( 156 sal_uInt16 nLength, const String& rText, sal_uInt16 nType) 157 { 158 DBG_ASSERT( nModelPosition >= 0, "illegal position" ); 159 DBG_ASSERT( (nModelPosition + nLength) <= pTxtNode->GetTxt().Len(), 160 "portion exceeds model string!" ); 161 162 DBG_ASSERT( !bFinished, "We are already done!" ); 163 164 // construct string with representation; either directly from 165 // rText, or use resources for special case portions 166 String sDisplay; 167 switch( nType ) 168 { 169 case POR_POSTITS: 170 //IAccessibility2 Implementation 2009----- 171 sDisplay = String(sal_Unicode(0xfffc)); 172 173 break; 174 case POR_FLYCNT: 175 sDisplay = String(sal_Unicode(0xfffc)); 176 break; 177 case POR_GRFNUM: 178 case POR_BULLET: 179 break; 180 case POR_FLD: 181 //Added by yanjun for 6854 182 case POR_HIDDEN: 183 case POR_COMBINED: 184 case POR_ISOREF: 185 //End 186 { 187 //When the filed content is empty, input a special character. 188 if (rText.Len() == 0) 189 sDisplay = String(sal_Unicode(0xfffc)); 190 else 191 sDisplay = rText; 192 aFieldPosition.push_back(aBuffer.getLength()); 193 aFieldPosition.push_back(aBuffer.getLength() + rText.Len()); 194 break; 195 } 196 case POR_FTNNUM: 197 { 198 break; 199 } 200 case POR_FTN: 201 { 202 sDisplay = rText; 203 sal_Int32 nStart=aBuffer.getLength(); 204 sal_Int32 nEnd=nStart + rText.Len(); 205 m_vecPairPos.push_back(std::make_pair(nStart,nEnd)); 206 break; 207 } 208 //-----IAccessibility2 Implementation 2009 209 break; 210 case POR_NUMBER: 211 { 212 OUStringBuffer aTmpBuffer( rText.Len() + 1 ); 213 aTmpBuffer.append( rText ); 214 aTmpBuffer.append( sal_Unicode(' ') ); 215 sDisplay = aTmpBuffer.makeStringAndClear(); 216 break; 217 } 218 // --> OD 2010-06-04 #i111768# - apply patch from kstribley: 219 // Include the control characters. 220 case POR_CONTROLCHAR: 221 { 222 OUStringBuffer aTmpBuffer( rText.Len() + 1 ); 223 aTmpBuffer.append( rText ); 224 aTmpBuffer.append( pTxtNode->GetTxt().GetChar(nModelPosition) ); 225 sDisplay = aTmpBuffer.makeStringAndClear(); 226 break; 227 } 228 // <-- 229 default: 230 sDisplay = rText; 231 break; 232 } 233 234 // ignore zero/zero portions (except for terminators) 235 if( (nLength == 0) && (sDisplay.Len() == 0) && (nType != POR_TERMINATE) ) 236 return; 237 238 // special treatment for zero length portion at the beginning: 239 // count as 'before' portion 240 if( ( nLength == 0 ) && ( nModelPosition == 0 ) ) 241 nBeforePortions++; 242 243 // store the 'old' positions 244 aModelPositions.push_back( nModelPosition ); 245 aAccessiblePositions.push_back( aBuffer.getLength() ); 246 247 // store portion attributes 248 sal_uInt8 nAttr = PORATTR_SPECIAL; 249 if( IsGrayPortionType(nType) ) nAttr |= PORATTR_GRAY; 250 if( nLength == 0 ) nAttr |= PORATTR_READONLY; 251 if( nType == POR_TERMINATE ) nAttr |= PORATTR_TERM; 252 aPortionAttrs.push_back( nAttr ); 253 254 // update buffer + nModelPosition 255 aBuffer.append( OUString(sDisplay) ); 256 nModelPosition += nLength; 257 258 // remember 'last' special portion (unless it's our own 'closing' 259 // portions from 'Finish()' 260 if( nType != POR_TERMINATE ) 261 bLastIsSpecial = sal_True; 262 } 263 264 void SwAccessiblePortionData::LineBreak() 265 { 266 DBG_ASSERT( !bFinished, "We are already done!" ); 267 268 aLineBreaks.push_back( aBuffer.getLength() ); 269 } 270 271 void SwAccessiblePortionData::Skip(sal_uInt16 nLength) 272 { 273 DBG_ASSERT( !bFinished, "We are already done!" ); 274 DBG_ASSERT( aModelPositions.size() == 0, "Never Skip() after portions" ); 275 DBG_ASSERT( nLength <= pTxtNode->GetTxt().Len(), "skip exceeds model string!" ); 276 277 nModelPosition += nLength; 278 } 279 280 void SwAccessiblePortionData::Finish() 281 { 282 DBG_ASSERT( !bFinished, "We are already done!" ); 283 284 // include terminator values: always include two 'last character' 285 // markers in the position arrays to make sure we always find one 286 // position before the end 287 Special( 0, String(), POR_TERMINATE ); 288 Special( 0, String(), POR_TERMINATE ); 289 LineBreak(); 290 LineBreak(); 291 292 sAccessibleString = aBuffer.makeStringAndClear(); 293 bFinished = sal_True; 294 } 295 296 297 sal_Bool SwAccessiblePortionData::IsPortionAttrSet( 298 size_t nPortionNo, sal_uInt8 nAttr ) const 299 { 300 DBG_ASSERT( nPortionNo < aPortionAttrs.size(), 301 "Illegal portion number" ); 302 return (aPortionAttrs[nPortionNo] & nAttr) != 0; 303 } 304 305 sal_Bool SwAccessiblePortionData::IsSpecialPortion( size_t nPortionNo ) const 306 { 307 return IsPortionAttrSet(nPortionNo, PORATTR_SPECIAL); 308 } 309 310 sal_Bool SwAccessiblePortionData::IsReadOnlyPortion( size_t nPortionNo ) const 311 { 312 return IsPortionAttrSet(nPortionNo, PORATTR_READONLY); 313 } 314 315 sal_Bool SwAccessiblePortionData::IsGrayPortionType( sal_uInt16 nType ) const 316 { 317 // gray portions? 318 // Compare with: inftxt.cxx, SwTxtPaintInfo::DrawViewOpt(...) 319 sal_Bool bGray = sal_False; 320 switch( nType ) 321 { 322 case POR_FTN: 323 case POR_ISOREF: 324 case POR_REF: 325 case POR_QUOVADIS: 326 case POR_NUMBER: 327 case POR_FLD: 328 case POR_URL: 329 case POR_INPUTFLD: 330 case POR_ISOTOX: 331 case POR_TOX: 332 case POR_HIDDEN: 333 bGray = !pViewOptions->IsPagePreview() && 334 !pViewOptions->IsReadonly() && SwViewOption::IsFieldShadings(); 335 break; 336 case POR_TAB: bGray = pViewOptions->IsTab(); break; 337 case POR_SOFTHYPH: bGray = pViewOptions->IsSoftHyph(); break; 338 case POR_BLANK: bGray = pViewOptions->IsHardBlank(); break; 339 default: 340 break; // bGray is false 341 } 342 return bGray; 343 } 344 345 346 const OUString& SwAccessiblePortionData::GetAccessibleString() const 347 { 348 DBG_ASSERT( bFinished, "Shouldn't call this before we are done!" ); 349 350 return sAccessibleString; 351 } 352 353 354 void SwAccessiblePortionData::GetLineBoundary( 355 Boundary& rBound, 356 sal_Int32 nPos ) const 357 { 358 FillBoundary( rBound, aLineBreaks, 359 FindBreak( aLineBreaks, nPos ) ); 360 } 361 362 // --> OD 2008-05-30 #i89175# 363 sal_Int32 SwAccessiblePortionData::GetLineCount() const 364 { 365 size_t nBreaks = aLineBreaks.size(); 366 // A non-empty paragraph has at least 4 breaks: one for each line3 and 367 // 3 additional ones. 368 // An empty paragraph has 3 breaks. 369 // Less than 3 breaks is an error case. 370 sal_Int32 nLineCount = ( nBreaks > 3 ) 371 ? nBreaks - 3 372 : ( ( nBreaks == 3 ) ? 1 : 0 ); 373 return nLineCount; 374 } 375 376 sal_Int32 SwAccessiblePortionData::GetLineNo( const sal_Int32 nPos ) const 377 { 378 sal_Int32 nLineNo = FindBreak( aLineBreaks, nPos ); 379 380 // handling of position after last character 381 const sal_Int32 nLineCount( GetLineCount() ); 382 if ( nLineNo >= nLineCount ) 383 { 384 nLineNo = nLineCount - 1; 385 } 386 387 return nLineNo; 388 } 389 390 void SwAccessiblePortionData::GetBoundaryOfLine( const sal_Int32 nLineNo, 391 i18n::Boundary& rLineBound ) 392 { 393 FillBoundary( rLineBound, aLineBreaks, nLineNo ); 394 } 395 // <-- 396 397 void SwAccessiblePortionData::GetLastLineBoundary( 398 Boundary& rBound ) const 399 { 400 DBG_ASSERT( aLineBreaks.size() >= 2, "need min + max value" ); 401 402 // The last two positions except the two deleimiters are the ones 403 // we are looking for, except for empty paragraphs (nBreaks==3) 404 size_t nBreaks = aLineBreaks.size(); 405 FillBoundary( rBound, aLineBreaks, nBreaks <= 3 ? 0 : nBreaks-4 ); 406 } 407 408 sal_uInt16 SwAccessiblePortionData::GetModelPosition( sal_Int32 nPos ) const 409 { 410 DBG_ASSERT( nPos >= 0, "illegal position" ); 411 DBG_ASSERT( nPos <= sAccessibleString.getLength(), "illegal position" ); 412 413 // find the portion number 414 size_t nPortionNo = FindBreak( aAccessiblePositions, nPos ); 415 416 // get model portion size 417 sal_Int32 nStartPos = aModelPositions[nPortionNo]; 418 419 // if it's a non-special portion, move into the portion, else 420 // return the portion start 421 if( ! IsSpecialPortion( nPortionNo ) ) 422 { 423 // 'wide' portions have to be of the same width 424 DBG_ASSERT( ( aModelPositions[nPortionNo+1] - nStartPos ) == 425 ( aAccessiblePositions[nPortionNo+1] - 426 aAccessiblePositions[nPortionNo] ), 427 "accesability portion disagrees with text model" ); 428 429 sal_Int32 nWithinPortion = nPos - aAccessiblePositions[nPortionNo]; 430 nStartPos += nWithinPortion; 431 } 432 // else: return nStartPos unmodified 433 434 DBG_ASSERT( (nStartPos >= 0) && (nStartPos < USHRT_MAX), 435 "How can the SwTxtNode have so many characters?" ); 436 return static_cast<sal_uInt16>(nStartPos); 437 } 438 439 void SwAccessiblePortionData::FillBoundary( 440 Boundary& rBound, 441 const Positions_t& rPositions, 442 size_t nPos ) const 443 { 444 rBound.startPos = rPositions[nPos]; 445 rBound.endPos = rPositions[nPos+1]; 446 } 447 448 449 size_t SwAccessiblePortionData::FindBreak( 450 const Positions_t& rPositions, 451 sal_Int32 nValue ) const 452 { 453 DBG_ASSERT( rPositions.size() >= 2, "need min + max value" ); 454 DBG_ASSERT( rPositions[0] <= nValue, "need min value" ); 455 DBG_ASSERT( rPositions[rPositions.size()-1] >= nValue, 456 "need first terminator value" ); 457 DBG_ASSERT( rPositions[rPositions.size()-2] >= nValue, 458 "need second terminator value" ); 459 460 size_t nMin = 0; 461 size_t nMax = rPositions.size()-2; 462 463 // loop until no more than two candidates are left 464 while( nMin+1 < nMax ) 465 { 466 // check loop invariants 467 DBG_ASSERT( ( (nMin == 0) && (rPositions[nMin] <= nValue) ) || 468 ( (nMin != 0) && (rPositions[nMin] < nValue) ), 469 "minvalue not minimal" ); 470 DBG_ASSERT( nValue <= rPositions[nMax], "max value not maximal" ); 471 472 // get middle (and ensure progress) 473 size_t nMiddle = (nMin + nMax)/2; 474 DBG_ASSERT( nMin < nMiddle, "progress?" ); 475 DBG_ASSERT( nMiddle < nMax, "progress?" ); 476 477 // check array 478 DBG_ASSERT( rPositions[nMin] <= rPositions[nMiddle], 479 "garbled positions array" ); 480 DBG_ASSERT( rPositions[nMiddle] <= rPositions[nMax], 481 "garbled positions array" ); 482 483 if( nValue > rPositions[nMiddle] ) 484 nMin = nMiddle; 485 else 486 nMax = nMiddle; 487 } 488 489 // only two are left; we only need to check which one is the winner 490 DBG_ASSERT( (nMax == nMin) || (nMax == nMin+1), "only two left" ); 491 if( (rPositions[nMin] < nValue) && (rPositions[nMin+1] <= nValue) ) 492 nMin = nMin+1; 493 494 // finally, check to see whether the returned value is the 'right' position 495 DBG_ASSERT( rPositions[nMin] <= nValue, "not smaller or equal" ); 496 DBG_ASSERT( nValue <= rPositions[nMin+1], "not equal or larger" ); 497 DBG_ASSERT( (nMin == 0) || (rPositions[nMin-1] <= nValue), 498 "earlier value should have been returned" ); 499 500 DBG_ASSERT( nMin < rPositions.size()-1, 501 "shouldn't return last position (due to termintator values)" ); 502 503 return nMin; 504 } 505 506 size_t SwAccessiblePortionData::FindLastBreak( 507 const Positions_t& rPositions, 508 sal_Int32 nValue ) const 509 { 510 size_t nResult = FindBreak( rPositions, nValue ); 511 512 // skip 'zero-length' portions 513 // --> OD 2006-10-19 #i70538# 514 // consider size of <rPosition> and ignore last entry 515 // while( rPositions[nResult+1] <= nValue ) 516 while ( nResult < rPositions.size() - 2 && 517 rPositions[nResult+1] <= nValue ) 518 { 519 nResult++; 520 } 521 // <-- 522 523 return nResult; 524 } 525 526 527 void SwAccessiblePortionData::GetSentenceBoundary( 528 Boundary& rBound, 529 sal_Int32 nPos ) 530 { 531 DBG_ASSERT( nPos >= 0, "illegal position; check before" ); 532 DBG_ASSERT( nPos < sAccessibleString.getLength(), "illegal position" ); 533 534 if( pSentences == NULL ) 535 { 536 DBG_ASSERT( pBreakIt != NULL, "We always need a break." ); 537 DBG_ASSERT( pBreakIt->GetBreakIter().is(), "No break-iterator." ); 538 if( pBreakIt->GetBreakIter().is() ) 539 { 540 pSentences = new Positions_t(); 541 pSentences->reserve(10); 542 543 // use xBreak->endOfSentence to iterate over all words; store 544 // positions in pSentences 545 sal_Int32 nCurrent = 0; 546 sal_Int32 nLength = sAccessibleString.getLength(); 547 do 548 { 549 pSentences->push_back( nCurrent ); 550 551 sal_uInt16 nModelPos = GetModelPosition( nCurrent ); 552 553 sal_Int32 nNew = pBreakIt->GetBreakIter()->endOfSentence( 554 sAccessibleString, nCurrent, 555 pBreakIt->GetLocale(pTxtNode->GetLang(nModelPos)) ) + 1; 556 557 if( (nNew < 0) && (nNew > nLength) ) 558 nNew = nLength; 559 else if (nNew <= nCurrent) 560 nNew = nCurrent + 1; // ensure forward progress 561 562 nCurrent = nNew; 563 } 564 while (nCurrent < nLength); 565 566 // finish with two terminators 567 pSentences->push_back( nLength ); 568 pSentences->push_back( nLength ); 569 } 570 else 571 { 572 // no break iterator -> empty word 573 rBound.startPos = 0; 574 rBound.endPos = 0; 575 return; 576 } 577 } 578 579 FillBoundary( rBound, *pSentences, FindBreak( *pSentences, nPos ) ); 580 } 581 582 void SwAccessiblePortionData::GetAttributeBoundary( 583 Boundary& rBound, 584 sal_Int32 nPos) const 585 { 586 DBG_ASSERT( pTxtNode != NULL, "Need SwTxtNode!" ); 587 588 // attribute boundaries can only occur on portion boundaries 589 FillBoundary( rBound, aAccessiblePositions, 590 FindBreak( aAccessiblePositions, nPos ) ); 591 } 592 593 594 sal_Int32 SwAccessiblePortionData::GetAccessiblePosition( sal_uInt16 nPos ) const 595 { 596 DBG_ASSERT( nPos <= pTxtNode->GetTxt().Len(), "illegal position" ); 597 598 // find the portion number 599 // --> OD 2006-10-19 #i70538# 600 // consider "empty" model portions - e.g. number portion 601 size_t nPortionNo = FindLastBreak( aModelPositions, 602 static_cast<sal_Int32>(nPos) ); 603 // <-- 604 605 sal_Int32 nRet = aAccessiblePositions[nPortionNo]; 606 607 // if the model portion has more than one position, go into it; 608 // else return that position 609 sal_Int32 nStartPos = aModelPositions[nPortionNo]; 610 sal_Int32 nEndPos = aModelPositions[nPortionNo+1]; 611 if( (nEndPos - nStartPos) > 1 ) 612 { 613 // 'wide' portions have to be of the same width 614 DBG_ASSERT( ( nEndPos - nStartPos ) == 615 ( aAccessiblePositions[nPortionNo+1] - 616 aAccessiblePositions[nPortionNo] ), 617 "accesability portion disagrees with text model" ); 618 619 sal_Int32 nWithinPortion = nPos - aModelPositions[nPortionNo]; 620 nRet += nWithinPortion; 621 } 622 // else: return nRet unmodified 623 624 DBG_ASSERT( (nRet >= 0) && (nRet <= sAccessibleString.getLength()), 625 "too long!" ); 626 return nRet; 627 } 628 629 sal_uInt16 SwAccessiblePortionData::FillSpecialPos( 630 sal_Int32 nPos, 631 SwSpecialPos& rPos, 632 SwSpecialPos*& rpPos ) const 633 { 634 size_t nPortionNo = FindLastBreak( aAccessiblePositions, nPos ); 635 636 sal_uInt8 nExtend(SP_EXTEND_RANGE_NONE); 637 sal_Int32 nRefPos(0); 638 sal_Int32 nModelPos(0); 639 640 if( nPortionNo < nBeforePortions ) 641 { 642 nExtend = SP_EXTEND_RANGE_BEFORE; 643 rpPos = &rPos; 644 } 645 else 646 { 647 sal_Int32 nModelEndPos = aModelPositions[nPortionNo+1]; 648 nModelPos = aModelPositions[nPortionNo]; 649 650 // skip backwards over zero-length portions, since GetCharRect() 651 // counts all model-zero-length portions as belonging to the 652 // previus portion 653 size_t nCorePortionNo = nPortionNo; 654 while( nModelPos == nModelEndPos ) 655 { 656 nCorePortionNo--; 657 nModelEndPos = nModelPos; 658 nModelPos = aModelPositions[nCorePortionNo]; 659 660 DBG_ASSERT( nModelPos >= 0, "Can't happen." ); 661 DBG_ASSERT( nCorePortionNo >= nBeforePortions, "Can't happen." ); 662 } 663 DBG_ASSERT( nModelPos != nModelEndPos, 664 "portion with core-representation expected" ); 665 666 // if we have anything except plain text, compute nExtend + nRefPos 667 if( (nModelEndPos - nModelPos == 1) && 668 (pTxtNode->GetTxt().GetChar(static_cast<sal_uInt16>(nModelPos)) != 669 sAccessibleString.getStr()[nPos]) ) 670 { 671 // case 1: a one-character, non-text portion 672 // reference position is the first accessibilty for our 673 // core portion 674 nRefPos = aAccessiblePositions[ nCorePortionNo ]; 675 nExtend = SP_EXTEND_RANGE_NONE; 676 rpPos = &rPos; 677 } 678 else if(nPortionNo != nCorePortionNo) 679 { 680 // case 2: a multi-character (text!) portion, followed by 681 // zero-length portions 682 // reference position is the first character of the next 683 // portion, and we are 'behind' 684 nRefPos = aAccessiblePositions[ nCorePortionNo+1 ]; 685 nExtend = SP_EXTEND_RANGE_BEHIND; 686 rpPos = &rPos; 687 } 688 else 689 { 690 // case 3: regular text portion 691 DBG_ASSERT( ( nModelEndPos - nModelPos ) == 692 ( aAccessiblePositions[nPortionNo+1] - 693 aAccessiblePositions[nPortionNo] ), 694 "text portion expected" ); 695 696 nModelPos += nPos - aAccessiblePositions[ nPortionNo ]; 697 rpPos = NULL; 698 } 699 } 700 if( rpPos != NULL ) 701 { 702 DBG_ASSERT( rpPos == &rPos, "Yes!" ); 703 DBG_ASSERT( nRefPos <= nPos, "wrong reference" ); 704 DBG_ASSERT( (nExtend == SP_EXTEND_RANGE_NONE) || 705 (nExtend == SP_EXTEND_RANGE_BEFORE) || 706 (nExtend == SP_EXTEND_RANGE_BEHIND), "need extend" ); 707 708 // get the line number, and adjust nRefPos for the line 709 // (if necessary) 710 size_t nRefLine = FindBreak( aLineBreaks, nRefPos ); 711 size_t nMyLine = FindBreak( aLineBreaks, nPos ); 712 sal_uInt16 nLineOffset = static_cast<sal_uInt16>( nMyLine - nRefLine ); 713 if( nLineOffset != 0 ) 714 nRefPos = aLineBreaks[ nMyLine ]; 715 716 // fill char offset and 'special position' 717 rPos.nCharOfst = static_cast<sal_uInt16>( nPos - nRefPos ); 718 rPos.nExtendRange = nExtend; 719 rPos.nLineOfst = nLineOffset; 720 } 721 722 return static_cast<sal_uInt16>( nModelPos ); 723 } 724 725 //IAccessibility2 Implementation 2009----- 726 sal_uInt16 SwAccessiblePortionData::GetAttrFldType( sal_Int32 nPos ) 727 { 728 if( aFieldPosition.size() < 2 ) return sal_False; 729 sal_Int32 nFieldIndex = 0; 730 for( size_t i = 0; i < aFieldPosition.size() - 1; i += 2 ) 731 { 732 if( nPos < aFieldPosition[ i + 1 ] && nPos >= aFieldPosition[ i ] ) 733 { 734 return aAttrFieldType[nFieldIndex]; 735 } 736 nFieldIndex++ ; 737 } 738 return 0; 739 } 740 741 sal_Bool SwAccessiblePortionData::FillBoundaryIFDateField( com::sun::star::i18n::Boundary& rBound, const sal_Int32 nPos ) 742 { 743 if( aFieldPosition.size() < 2 ) return sal_False; 744 for( size_t i = 0; i < aFieldPosition.size() - 1; i += 2 ) 745 { 746 if( nPos < aFieldPosition[ i + 1 ] && nPos >= aFieldPosition[ i ] ) 747 { 748 rBound.startPos = aFieldPosition[i]; 749 rBound.endPos = aFieldPosition[i + 1]; 750 return sal_True; 751 } 752 } 753 return sal_False; 754 } 755 //-----IAccessibility2 Implementation 2009 756 void SwAccessiblePortionData::AdjustAndCheck( 757 sal_Int32 nPos, 758 size_t& nPortionNo, 759 sal_uInt16& nCorePos, 760 sal_Bool& bEdit) const 761 { 762 // find portion and get mode position 763 nPortionNo = FindBreak( aAccessiblePositions, nPos ); 764 nCorePos = static_cast<sal_uInt16>( aModelPositions[ nPortionNo ] ); 765 766 // for special portions, make sure we're on a portion boundary 767 // for text portions, add the in-portion offset 768 if( IsSpecialPortion( nPortionNo ) ) 769 bEdit &= nPos == aAccessiblePositions[nPortionNo]; 770 else 771 nCorePos = static_cast<sal_uInt16>( nCorePos + 772 nPos - aAccessiblePositions[nPortionNo] ); 773 } 774 775 sal_Bool SwAccessiblePortionData::GetEditableRange( 776 sal_Int32 nStart, sal_Int32 nEnd, 777 sal_uInt16& nCoreStart, sal_uInt16& nCoreEnd ) const 778 { 779 sal_Bool bIsEditable = sal_True; 780 781 // get start and end portions 782 size_t nStartPortion, nEndPortion; 783 AdjustAndCheck( nStart, nStartPortion, nCoreStart, bIsEditable ); 784 AdjustAndCheck( nEnd, nEndPortion, nCoreEnd, bIsEditable ); 785 786 // iterate over portions, and make sure there is no read-only portion 787 // in-between 788 size_t nLastPortion = nEndPortion; 789 790 // don't count last portion if we're in front of a special portion 791 if( IsSpecialPortion(nLastPortion) ) 792 { 793 if (nLastPortion > 0) 794 nLastPortion--; 795 else 796 // special case: because size_t is usually unsigned, we can't just 797 // decrease nLastPortion to -1 (which would normally do the job, so 798 // this whole if wouldn't be needed). Instead, we'll do this 799 // special case and just increae the start portion beyond the last 800 // portion to make sure the loop below will have zero iteration. 801 nStartPortion = nLastPortion + 1; 802 } 803 804 for( size_t nPor = nStartPortion; nPor <= nLastPortion; nPor ++ ) 805 { 806 bIsEditable &= ! IsReadOnlyPortion( nPor ); 807 } 808 809 return bIsEditable; 810 } 811 812 sal_Bool SwAccessiblePortionData::IsValidCorePosition( sal_uInt16 nPos ) const 813 { 814 // a position is valid its within the model positions that we know 815 return ( aModelPositions[0] <= nPos ) && 816 ( nPos <= aModelPositions[ aModelPositions.size()-1 ] ); 817 } 818 819 //IAccessibility2 Implementation 2009----- 820 sal_Bool SwAccessiblePortionData::IsZeroCorePositionData() 821 { 822 if( aModelPositions.size() < 1 ) return sal_True; 823 return aModelPositions[0] == 0 && aModelPositions[aModelPositions.size()-1] == 0; 824 } 825 826 sal_Bool SwAccessiblePortionData::IsIndexInFootnode(sal_Int32 nIndex) 827 { 828 VEC_PAIR_POS::iterator vi =m_vecPairPos.begin(); 829 for (;vi != m_vecPairPos.end() ; ++vi) 830 { 831 const PAIR_POS &pairPos = *vi; 832 if(nIndex >= pairPos.first && nIndex < pairPos.second ) 833 { 834 return sal_True; 835 } 836 } 837 return sal_False; 838 } 839 840 sal_Bool SwAccessiblePortionData::IsInGrayPortion( sal_Int32 nPos ) 841 { 842 // return IsGrayPortion( FindBreak( aAccessiblePositions, nPos ) ); 843 return IsPortionAttrSet( FindBreak( aAccessiblePositions, nPos ), 844 PORATTR_GRAY ); 845 } 846 847 sal_Int32 SwAccessiblePortionData::GetFieldIndex(sal_Int32 nPos) 848 { 849 sal_Int32 nIndex = -1; 850 if( aFieldPosition.size() >= 2 ) 851 { 852 for( sal_Int32 i = 0; i < aFieldPosition.size() - 1; i += 2 ) 853 { 854 if( nPos <= aFieldPosition[ i + 1 ] && nPos >= aFieldPosition[ i ] ) 855 { 856 nIndex = i/2; 857 break; 858 } 859 } 860 } 861 return nIndex; 862 } 863 sal_uInt16 SwAccessiblePortionData::GetFirstValidCorePosition() const 864 { 865 return static_cast<sal_uInt16>( aModelPositions[0] ); 866 } 867 868 sal_uInt16 SwAccessiblePortionData::GetLastValidCorePosition() const 869 { 870 return static_cast<sal_uInt16>( aModelPositions[ aModelPositions.size()-1 ] ); 871 } 872