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