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_ISOTOX: 330 case POR_TOX: 331 case POR_HIDDEN: 332 bGray = !pViewOptions->IsPagePreview() && 333 !pViewOptions->IsReadonly() && SwViewOption::IsFieldShadings(); 334 break; 335 case POR_TAB: bGray = pViewOptions->IsTab(); break; 336 case POR_SOFTHYPH: bGray = pViewOptions->IsSoftHyph(); break; 337 case POR_BLANK: bGray = pViewOptions->IsHardBlank(); break; 338 default: 339 break; // bGray is false 340 } 341 return bGray; 342 } 343 344 345 const OUString& SwAccessiblePortionData::GetAccessibleString() const 346 { 347 DBG_ASSERT( bFinished, "Shouldn't call this before we are done!" ); 348 349 return sAccessibleString; 350 } 351 352 353 void SwAccessiblePortionData::GetLineBoundary( 354 Boundary& rBound, 355 sal_Int32 nPos ) const 356 { 357 FillBoundary( rBound, aLineBreaks, 358 FindBreak( aLineBreaks, nPos ) ); 359 } 360 361 // --> OD 2008-05-30 #i89175# 362 sal_Int32 SwAccessiblePortionData::GetLineCount() const 363 { 364 size_t nBreaks = aLineBreaks.size(); 365 // A non-empty paragraph has at least 4 breaks: one for each line3 and 366 // 3 additional ones. 367 // An empty paragraph has 3 breaks. 368 // Less than 3 breaks is an error case. 369 sal_Int32 nLineCount = ( nBreaks > 3 ) 370 ? nBreaks - 3 371 : ( ( nBreaks == 3 ) ? 1 : 0 ); 372 return nLineCount; 373 } 374 375 sal_Int32 SwAccessiblePortionData::GetLineNo( const sal_Int32 nPos ) const 376 { 377 sal_Int32 nLineNo = FindBreak( aLineBreaks, nPos ); 378 379 // handling of position after last character 380 const sal_Int32 nLineCount( GetLineCount() ); 381 if ( nLineNo >= nLineCount ) 382 { 383 nLineNo = nLineCount - 1; 384 } 385 386 return nLineNo; 387 } 388 389 void SwAccessiblePortionData::GetBoundaryOfLine( const sal_Int32 nLineNo, 390 i18n::Boundary& rLineBound ) 391 { 392 FillBoundary( rLineBound, aLineBreaks, nLineNo ); 393 } 394 // <-- 395 396 void SwAccessiblePortionData::GetLastLineBoundary( 397 Boundary& rBound ) const 398 { 399 DBG_ASSERT( aLineBreaks.size() >= 2, "need min + max value" ); 400 401 // The last two positions except the two deleimiters are the ones 402 // we are looking for, except for empty paragraphs (nBreaks==3) 403 size_t nBreaks = aLineBreaks.size(); 404 FillBoundary( rBound, aLineBreaks, nBreaks <= 3 ? 0 : nBreaks-4 ); 405 } 406 407 sal_uInt16 SwAccessiblePortionData::GetModelPosition( sal_Int32 nPos ) const 408 { 409 DBG_ASSERT( nPos >= 0, "illegal position" ); 410 DBG_ASSERT( nPos <= sAccessibleString.getLength(), "illegal position" ); 411 412 // find the portion number 413 size_t nPortionNo = FindBreak( aAccessiblePositions, nPos ); 414 415 // get model portion size 416 sal_Int32 nStartPos = aModelPositions[nPortionNo]; 417 418 // if it's a non-special portion, move into the portion, else 419 // return the portion start 420 if( ! IsSpecialPortion( nPortionNo ) ) 421 { 422 // 'wide' portions have to be of the same width 423 DBG_ASSERT( ( aModelPositions[nPortionNo+1] - nStartPos ) == 424 ( aAccessiblePositions[nPortionNo+1] - 425 aAccessiblePositions[nPortionNo] ), 426 "accesability portion disagrees with text model" ); 427 428 sal_Int32 nWithinPortion = nPos - aAccessiblePositions[nPortionNo]; 429 nStartPos += nWithinPortion; 430 } 431 // else: return nStartPos unmodified 432 433 DBG_ASSERT( (nStartPos >= 0) && (nStartPos < USHRT_MAX), 434 "How can the SwTxtNode have so many characters?" ); 435 return static_cast<sal_uInt16>(nStartPos); 436 } 437 438 void SwAccessiblePortionData::FillBoundary( 439 Boundary& rBound, 440 const Positions_t& rPositions, 441 size_t nPos ) const 442 { 443 rBound.startPos = rPositions[nPos]; 444 rBound.endPos = rPositions[nPos+1]; 445 } 446 447 448 size_t SwAccessiblePortionData::FindBreak( 449 const Positions_t& rPositions, 450 sal_Int32 nValue ) const 451 { 452 DBG_ASSERT( rPositions.size() >= 2, "need min + max value" ); 453 DBG_ASSERT( rPositions[0] <= nValue, "need min value" ); 454 DBG_ASSERT( rPositions[rPositions.size()-1] >= nValue, 455 "need first terminator value" ); 456 DBG_ASSERT( rPositions[rPositions.size()-2] >= nValue, 457 "need second terminator value" ); 458 459 size_t nMin = 0; 460 size_t nMax = rPositions.size()-2; 461 462 // loop until no more than two candidates are left 463 while( nMin+1 < nMax ) 464 { 465 // check loop invariants 466 DBG_ASSERT( ( (nMin == 0) && (rPositions[nMin] <= nValue) ) || 467 ( (nMin != 0) && (rPositions[nMin] < nValue) ), 468 "minvalue not minimal" ); 469 DBG_ASSERT( nValue <= rPositions[nMax], "max value not maximal" ); 470 471 // get middle (and ensure progress) 472 size_t nMiddle = (nMin + nMax)/2; 473 DBG_ASSERT( nMin < nMiddle, "progress?" ); 474 DBG_ASSERT( nMiddle < nMax, "progress?" ); 475 476 // check array 477 DBG_ASSERT( rPositions[nMin] <= rPositions[nMiddle], 478 "garbled positions array" ); 479 DBG_ASSERT( rPositions[nMiddle] <= rPositions[nMax], 480 "garbled positions array" ); 481 482 if( nValue > rPositions[nMiddle] ) 483 nMin = nMiddle; 484 else 485 nMax = nMiddle; 486 } 487 488 // only two are left; we only need to check which one is the winner 489 DBG_ASSERT( (nMax == nMin) || (nMax == nMin+1), "only two left" ); 490 if( (rPositions[nMin] < nValue) && (rPositions[nMin+1] <= nValue) ) 491 nMin = nMin+1; 492 493 // finally, check to see whether the returned value is the 'right' position 494 DBG_ASSERT( rPositions[nMin] <= nValue, "not smaller or equal" ); 495 DBG_ASSERT( nValue <= rPositions[nMin+1], "not equal or larger" ); 496 DBG_ASSERT( (nMin == 0) || (rPositions[nMin-1] <= nValue), 497 "earlier value should have been returned" ); 498 499 DBG_ASSERT( nMin < rPositions.size()-1, 500 "shouldn't return last position (due to termintator values)" ); 501 502 return nMin; 503 } 504 505 size_t SwAccessiblePortionData::FindLastBreak( 506 const Positions_t& rPositions, 507 sal_Int32 nValue ) const 508 { 509 size_t nResult = FindBreak( rPositions, nValue ); 510 511 // skip 'zero-length' portions 512 // --> OD 2006-10-19 #i70538# 513 // consider size of <rPosition> and ignore last entry 514 // while( rPositions[nResult+1] <= nValue ) 515 while ( nResult < rPositions.size() - 2 && 516 rPositions[nResult+1] <= nValue ) 517 { 518 nResult++; 519 } 520 // <-- 521 522 return nResult; 523 } 524 525 526 void SwAccessiblePortionData::GetSentenceBoundary( 527 Boundary& rBound, 528 sal_Int32 nPos ) 529 { 530 DBG_ASSERT( nPos >= 0, "illegal position; check before" ); 531 DBG_ASSERT( nPos < sAccessibleString.getLength(), "illegal position" ); 532 533 if( pSentences == NULL ) 534 { 535 DBG_ASSERT( pBreakIt != NULL, "We always need a break." ); 536 DBG_ASSERT( pBreakIt->GetBreakIter().is(), "No break-iterator." ); 537 if( pBreakIt->GetBreakIter().is() ) 538 { 539 pSentences = new Positions_t(); 540 pSentences->reserve(10); 541 542 // use xBreak->endOfSentence to iterate over all words; store 543 // positions in pSentences 544 sal_Int32 nCurrent = 0; 545 sal_Int32 nLength = sAccessibleString.getLength(); 546 do 547 { 548 pSentences->push_back( nCurrent ); 549 550 sal_uInt16 nModelPos = GetModelPosition( nCurrent ); 551 552 sal_Int32 nNew = pBreakIt->GetBreakIter()->endOfSentence( 553 sAccessibleString, nCurrent, 554 pBreakIt->GetLocale(pTxtNode->GetLang(nModelPos)) ) + 1; 555 556 if( (nNew < 0) && (nNew > nLength) ) 557 nNew = nLength; 558 else if (nNew <= nCurrent) 559 nNew = nCurrent + 1; // ensure forward progress 560 561 nCurrent = nNew; 562 } 563 while (nCurrent < nLength); 564 565 // finish with two terminators 566 pSentences->push_back( nLength ); 567 pSentences->push_back( nLength ); 568 } 569 else 570 { 571 // no break iterator -> empty word 572 rBound.startPos = 0; 573 rBound.endPos = 0; 574 return; 575 } 576 } 577 578 FillBoundary( rBound, *pSentences, FindBreak( *pSentences, nPos ) ); 579 } 580 581 void SwAccessiblePortionData::GetAttributeBoundary( 582 Boundary& rBound, 583 sal_Int32 nPos) const 584 { 585 DBG_ASSERT( pTxtNode != NULL, "Need SwTxtNode!" ); 586 587 // attribute boundaries can only occur on portion boundaries 588 FillBoundary( rBound, aAccessiblePositions, 589 FindBreak( aAccessiblePositions, nPos ) ); 590 } 591 592 593 sal_Int32 SwAccessiblePortionData::GetAccessiblePosition( sal_uInt16 nPos ) const 594 { 595 DBG_ASSERT( nPos <= pTxtNode->GetTxt().Len(), "illegal position" ); 596 597 // find the portion number 598 // --> OD 2006-10-19 #i70538# 599 // consider "empty" model portions - e.g. number portion 600 size_t nPortionNo = FindLastBreak( aModelPositions, 601 static_cast<sal_Int32>(nPos) ); 602 // <-- 603 604 sal_Int32 nRet = aAccessiblePositions[nPortionNo]; 605 606 // if the model portion has more than one position, go into it; 607 // else return that position 608 sal_Int32 nStartPos = aModelPositions[nPortionNo]; 609 sal_Int32 nEndPos = aModelPositions[nPortionNo+1]; 610 if( (nEndPos - nStartPos) > 1 ) 611 { 612 // 'wide' portions have to be of the same width 613 DBG_ASSERT( ( nEndPos - nStartPos ) == 614 ( aAccessiblePositions[nPortionNo+1] - 615 aAccessiblePositions[nPortionNo] ), 616 "accesability portion disagrees with text model" ); 617 618 sal_Int32 nWithinPortion = nPos - aModelPositions[nPortionNo]; 619 nRet += nWithinPortion; 620 } 621 // else: return nRet unmodified 622 623 DBG_ASSERT( (nRet >= 0) && (nRet <= sAccessibleString.getLength()), 624 "too long!" ); 625 return nRet; 626 } 627 628 sal_uInt16 SwAccessiblePortionData::FillSpecialPos( 629 sal_Int32 nPos, 630 SwSpecialPos& rPos, 631 SwSpecialPos*& rpPos ) const 632 { 633 size_t nPortionNo = FindLastBreak( aAccessiblePositions, nPos ); 634 635 sal_uInt8 nExtend(SP_EXTEND_RANGE_NONE); 636 sal_Int32 nRefPos(0); 637 sal_Int32 nModelPos(0); 638 639 if( nPortionNo < nBeforePortions ) 640 { 641 nExtend = SP_EXTEND_RANGE_BEFORE; 642 rpPos = &rPos; 643 } 644 else 645 { 646 sal_Int32 nModelEndPos = aModelPositions[nPortionNo+1]; 647 nModelPos = aModelPositions[nPortionNo]; 648 649 // skip backwards over zero-length portions, since GetCharRect() 650 // counts all model-zero-length portions as belonging to the 651 // previus portion 652 size_t nCorePortionNo = nPortionNo; 653 while( nModelPos == nModelEndPos ) 654 { 655 nCorePortionNo--; 656 nModelEndPos = nModelPos; 657 nModelPos = aModelPositions[nCorePortionNo]; 658 659 DBG_ASSERT( nModelPos >= 0, "Can't happen." ); 660 DBG_ASSERT( nCorePortionNo >= nBeforePortions, "Can't happen." ); 661 } 662 DBG_ASSERT( nModelPos != nModelEndPos, 663 "portion with core-representation expected" ); 664 665 // if we have anything except plain text, compute nExtend + nRefPos 666 if( (nModelEndPos - nModelPos == 1) && 667 (pTxtNode->GetTxt().GetChar(static_cast<sal_uInt16>(nModelPos)) != 668 sAccessibleString.getStr()[nPos]) ) 669 { 670 // case 1: a one-character, non-text portion 671 // reference position is the first accessibilty for our 672 // core portion 673 nRefPos = aAccessiblePositions[ nCorePortionNo ]; 674 nExtend = SP_EXTEND_RANGE_NONE; 675 rpPos = &rPos; 676 } 677 else if(nPortionNo != nCorePortionNo) 678 { 679 // case 2: a multi-character (text!) portion, followed by 680 // zero-length portions 681 // reference position is the first character of the next 682 // portion, and we are 'behind' 683 nRefPos = aAccessiblePositions[ nCorePortionNo+1 ]; 684 nExtend = SP_EXTEND_RANGE_BEHIND; 685 rpPos = &rPos; 686 } 687 else 688 { 689 // case 3: regular text portion 690 DBG_ASSERT( ( nModelEndPos - nModelPos ) == 691 ( aAccessiblePositions[nPortionNo+1] - 692 aAccessiblePositions[nPortionNo] ), 693 "text portion expected" ); 694 695 nModelPos += nPos - aAccessiblePositions[ nPortionNo ]; 696 rpPos = NULL; 697 } 698 } 699 if( rpPos != NULL ) 700 { 701 DBG_ASSERT( rpPos == &rPos, "Yes!" ); 702 DBG_ASSERT( nRefPos <= nPos, "wrong reference" ); 703 DBG_ASSERT( (nExtend == SP_EXTEND_RANGE_NONE) || 704 (nExtend == SP_EXTEND_RANGE_BEFORE) || 705 (nExtend == SP_EXTEND_RANGE_BEHIND), "need extend" ); 706 707 // get the line number, and adjust nRefPos for the line 708 // (if necessary) 709 size_t nRefLine = FindBreak( aLineBreaks, nRefPos ); 710 size_t nMyLine = FindBreak( aLineBreaks, nPos ); 711 sal_uInt16 nLineOffset = static_cast<sal_uInt16>( nMyLine - nRefLine ); 712 if( nLineOffset != 0 ) 713 nRefPos = aLineBreaks[ nMyLine ]; 714 715 // fill char offset and 'special position' 716 rPos.nCharOfst = static_cast<sal_uInt16>( nPos - nRefPos ); 717 rPos.nExtendRange = nExtend; 718 rPos.nLineOfst = nLineOffset; 719 } 720 721 return static_cast<sal_uInt16>( nModelPos ); 722 } 723 724 //IAccessibility2 Implementation 2009----- 725 sal_uInt16 SwAccessiblePortionData::GetAttrFldType( sal_Int32 nPos ) 726 { 727 if( aFieldPosition.size() < 2 ) return sal_False; 728 sal_Int32 nFieldIndex = 0; 729 for( size_t i = 0; i < aFieldPosition.size() - 1; i += 2 ) 730 { 731 if( nPos < aFieldPosition[ i + 1 ] && nPos >= aFieldPosition[ i ] ) 732 { 733 return aAttrFieldType[nFieldIndex]; 734 } 735 nFieldIndex++ ; 736 } 737 return 0; 738 } 739 740 sal_Bool SwAccessiblePortionData::FillBoundaryIFDateField( com::sun::star::i18n::Boundary& rBound, const sal_Int32 nPos ) 741 { 742 if( aFieldPosition.size() < 2 ) return sal_False; 743 for( size_t i = 0; i < aFieldPosition.size() - 1; i += 2 ) 744 { 745 if( nPos < aFieldPosition[ i + 1 ] && nPos >= aFieldPosition[ i ] ) 746 { 747 rBound.startPos = aFieldPosition[i]; 748 rBound.endPos = aFieldPosition[i + 1]; 749 return sal_True; 750 } 751 } 752 return sal_False; 753 } 754 //-----IAccessibility2 Implementation 2009 755 void SwAccessiblePortionData::AdjustAndCheck( 756 sal_Int32 nPos, 757 size_t& nPortionNo, 758 sal_uInt16& nCorePos, 759 sal_Bool& bEdit) const 760 { 761 // find portion and get mode position 762 nPortionNo = FindBreak( aAccessiblePositions, nPos ); 763 nCorePos = static_cast<sal_uInt16>( aModelPositions[ nPortionNo ] ); 764 765 // for special portions, make sure we're on a portion boundary 766 // for text portions, add the in-portion offset 767 if( IsSpecialPortion( nPortionNo ) ) 768 bEdit &= nPos == aAccessiblePositions[nPortionNo]; 769 else 770 nCorePos = static_cast<sal_uInt16>( nCorePos + 771 nPos - aAccessiblePositions[nPortionNo] ); 772 } 773 774 sal_Bool SwAccessiblePortionData::GetEditableRange( 775 sal_Int32 nStart, sal_Int32 nEnd, 776 sal_uInt16& nCoreStart, sal_uInt16& nCoreEnd ) const 777 { 778 sal_Bool bIsEditable = sal_True; 779 780 // get start and end portions 781 size_t nStartPortion, nEndPortion; 782 AdjustAndCheck( nStart, nStartPortion, nCoreStart, bIsEditable ); 783 AdjustAndCheck( nEnd, nEndPortion, nCoreEnd, bIsEditable ); 784 785 // iterate over portions, and make sure there is no read-only portion 786 // in-between 787 size_t nLastPortion = nEndPortion; 788 789 // don't count last portion if we're in front of a special portion 790 if( IsSpecialPortion(nLastPortion) ) 791 { 792 if (nLastPortion > 0) 793 nLastPortion--; 794 else 795 // special case: because size_t is usually unsigned, we can't just 796 // decrease nLastPortion to -1 (which would normally do the job, so 797 // this whole if wouldn't be needed). Instead, we'll do this 798 // special case and just increae the start portion beyond the last 799 // portion to make sure the loop below will have zero iteration. 800 nStartPortion = nLastPortion + 1; 801 } 802 803 for( size_t nPor = nStartPortion; nPor <= nLastPortion; nPor ++ ) 804 { 805 bIsEditable &= ! IsReadOnlyPortion( nPor ); 806 } 807 808 return bIsEditable; 809 } 810 811 sal_Bool SwAccessiblePortionData::IsValidCorePosition( sal_uInt16 nPos ) const 812 { 813 // a position is valid its within the model positions that we know 814 return ( aModelPositions[0] <= nPos ) && 815 ( nPos <= aModelPositions[ aModelPositions.size()-1 ] ); 816 } 817 818 //IAccessibility2 Implementation 2009----- 819 sal_Bool SwAccessiblePortionData::IsZeroCorePositionData() 820 { 821 if( aModelPositions.size() < 1 ) return sal_True; 822 return aModelPositions[0] == 0 && aModelPositions[aModelPositions.size()-1] == 0; 823 } 824 825 sal_Bool SwAccessiblePortionData::IsIndexInFootnode(sal_Int32 nIndex) 826 { 827 VEC_PAIR_POS::iterator vi =m_vecPairPos.begin(); 828 for (;vi != m_vecPairPos.end() ; ++vi) 829 { 830 const PAIR_POS &pairPos = *vi; 831 if(nIndex >= pairPos.first && nIndex < pairPos.second ) 832 { 833 return sal_True; 834 } 835 } 836 return sal_False; 837 } 838 839 sal_Bool SwAccessiblePortionData::IsInGrayPortion( sal_Int32 nPos ) 840 { 841 // return IsGrayPortion( FindBreak( aAccessiblePositions, nPos ) ); 842 return IsPortionAttrSet( FindBreak( aAccessiblePositions, nPos ), 843 PORATTR_GRAY ); 844 } 845 846 sal_Int32 SwAccessiblePortionData::GetFieldIndex(sal_Int32 nPos) 847 { 848 sal_Int32 nIndex = -1; 849 if( aFieldPosition.size() >= 2 ) 850 { 851 for( sal_Int32 i = 0; i < aFieldPosition.size() - 1; i += 2 ) 852 { 853 if( nPos <= aFieldPosition[ i + 1 ] && nPos >= aFieldPosition[ i ] ) 854 { 855 nIndex = i/2; 856 break; 857 } 858 } 859 } 860 return nIndex; 861 } 862 sal_uInt16 SwAccessiblePortionData::GetFirstValidCorePosition() const 863 { 864 return static_cast<sal_uInt16>( aModelPositions[0] ); 865 } 866 867 sal_uInt16 SwAccessiblePortionData::GetLastValidCorePosition() const 868 { 869 return static_cast<sal_uInt16>( aModelPositions[ aModelPositions.size()-1 ] ); 870 } 871