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
SwAccessiblePortionData(const SwTxtNode * pTxtNd,const SwViewOption * pViewOpt)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
~SwAccessiblePortionData()112 SwAccessiblePortionData::~SwAccessiblePortionData()
113 {
114 delete pSentences;
115 }
116
Text(sal_uInt16 nLength,sal_uInt16 nType)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 }
SetAttrFieldType(sal_uInt16 nAttrFldType)145 void SwAccessiblePortionData::SetAttrFieldType( sal_uInt16 nAttrFldType )
146 {
147 aAttrFieldType.push_back(nAttrFldType);
148 return;
149 }
150
Special(sal_uInt16 nLength,const String & rText,sal_uInt16 nType)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
LineBreak()258 void SwAccessiblePortionData::LineBreak()
259 {
260 DBG_ASSERT( !bFinished, "We are already done!" );
261
262 aLineBreaks.push_back( aBuffer.getLength() );
263 }
264
Skip(sal_uInt16 nLength)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
Finish()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
IsPortionAttrSet(size_t nPortionNo,sal_uInt8 nAttr) const291 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
IsSpecialPortion(size_t nPortionNo) const299 sal_Bool SwAccessiblePortionData::IsSpecialPortion( size_t nPortionNo ) const
300 {
301 return IsPortionAttrSet(nPortionNo, PORATTR_SPECIAL);
302 }
303
IsReadOnlyPortion(size_t nPortionNo) const304 sal_Bool SwAccessiblePortionData::IsReadOnlyPortion( size_t nPortionNo ) const
305 {
306 return IsPortionAttrSet(nPortionNo, PORATTR_READONLY);
307 }
308
IsGrayPortionType(sal_uInt16 nType) const309 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
GetAccessibleString() const340 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
GetLineBoundary(Boundary & rBound,sal_Int32 nPos) const348 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#
GetLineCount() const357 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
GetLineNo(const sal_Int32 nPos) const370 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
GetBoundaryOfLine(const sal_Int32 nLineNo,i18n::Boundary & rLineBound)384 void SwAccessiblePortionData::GetBoundaryOfLine( const sal_Int32 nLineNo,
385 i18n::Boundary& rLineBound )
386 {
387 FillBoundary( rLineBound, aLineBreaks, nLineNo );
388 }
389 // <--
390
GetLastLineBoundary(Boundary & rBound) const391 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
GetModelPosition(sal_Int32 nPos) const402 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
FillBoundary(Boundary & rBound,const Positions_t & rPositions,size_t nPos) const433 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
FindBreak(const Positions_t & rPositions,sal_Int32 nValue) const443 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
FindLastBreak(const Positions_t & rPositions,sal_Int32 nValue) const500 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
GetSentenceBoundary(Boundary & rBound,sal_Int32 nPos)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
GetAttributeBoundary(Boundary & rBound,sal_Int32 nPos) const576 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
GetAccessiblePosition(sal_uInt16 nPos) const588 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
FillSpecialPos(sal_Int32 nPos,SwSpecialPos & rPos,SwSpecialPos * & rpPos) const623 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
GetAttrFldType(sal_Int32 nPos)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
FillBoundaryIFDateField(com::sun::star::i18n::Boundary & rBound,const sal_Int32 nPos)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 }
AdjustAndCheck(sal_Int32 nPos,size_t & nPortionNo,sal_uInt16 & nCorePos,sal_Bool & bEdit) const748 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
GetEditableRange(sal_Int32 nStart,sal_Int32 nEnd,sal_uInt16 & nCoreStart,sal_uInt16 & nCoreEnd) const767 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
IsValidCorePosition(sal_uInt16 nPos) const804 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
IsZeroCorePositionData()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
IsIndexInFootnode(sal_Int32 nIndex)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
IsInGrayPortion(sal_Int32 nPos)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
GetFieldIndex(sal_Int32 nPos)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 }
GetFirstValidCorePosition() const854 sal_uInt16 SwAccessiblePortionData::GetFirstValidCorePosition() const
855 {
856 return static_cast<sal_uInt16>( aModelPositions[0] );
857 }
858
GetLastValidCorePosition() const859 sal_uInt16 SwAccessiblePortionData::GetLastValidCorePosition() const
860 {
861 return static_cast<sal_uInt16>( aModelPositions[ aModelPositions.size()-1 ] );
862 }
863