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