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