xref: /AOO41X/main/sw/source/core/access/accportions.cxx (revision c4020a14fb899b7281b543eb0d493bdf45ac7cec)
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