Line data Source code
1 : /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 : /*
3 : * This file is part of the LibreOffice project.
4 : *
5 : * This Source Code Form is subject to the terms of the Mozilla Public
6 : * License, v. 2.0. If a copy of the MPL was not distributed with this
7 : * file, You can obtain one at http://mozilla.org/MPL/2.0/.
8 : *
9 : * This file incorporates work covered by the following license notice:
10 : *
11 : * Licensed to the Apache Software Foundation (ASF) under one or more
12 : * contributor license agreements. See the NOTICE file distributed
13 : * with this work for additional information regarding copyright
14 : * ownership. The ASF licenses this file to you under the Apache
15 : * License, Version 2.0 (the "License"); you may not use this file
16 : * except in compliance with the License. You may obtain a copy of
17 : * the License at http://www.apache.org/licenses/LICENSE-2.0 .
18 : */
19 :
20 : #include "accportions.hxx"
21 : #include <rtl/ustring.hxx>
22 : #include <com/sun/star/i18n/Boundary.hpp>
23 : #include <txttypes.hxx>
24 :
25 : // for portion replacement in Special()
26 : #include "access.hrc"
27 : #include <tools/resid.hxx>
28 : #include "viewopt.hxx"
29 :
30 : // for GetWordBoundary(...), GetSentenceBoundary(...):
31 : #include <breakit.hxx>
32 : #include <com/sun/star/i18n/WordType.hpp>
33 : #include <com/sun/star/i18n/XBreakIterator.hpp>
34 : #include <ndtxt.hxx>
35 :
36 : // for FillSpecialPos(...)
37 : #include "crstate.hxx"
38 :
39 : // for SwAccessibleContext::GetResource()
40 : #include "acccontext.hxx"
41 :
42 : // for Post-It replacement text:
43 : #include "txatbase.hxx"
44 : #include "fmtfld.hxx"
45 : #include "fldbas.hxx"
46 : #include "docufld.hxx"
47 :
48 : // for in-line graphics replacement:
49 : #include "ndindex.hxx"
50 : #include "ndnotxt.hxx"
51 : #include "fmtflcnt.hxx"
52 : #include "frmfmt.hxx"
53 : #include "fmtcntnt.hxx"
54 :
55 : using namespace ::com::sun::star;
56 :
57 : using i18n::Boundary;
58 :
59 : // 'portion type' for terminating portions
60 : #define POR_TERMINATE 0
61 :
62 : // portion attributes
63 : #define PORATTR_SPECIAL 1
64 : #define PORATTR_READONLY 2
65 : #define PORATTR_GRAY 4
66 : #define PORATTR_TERM 128
67 :
68 28 : SwAccessiblePortionData::SwAccessiblePortionData(
69 : const SwTxtNode* pTxtNd,
70 : const SwViewOption* pViewOpt ) :
71 : SwPortionHandler(),
72 : pTxtNode( pTxtNd ),
73 : aBuffer(),
74 : nModelPosition( 0 ),
75 : bFinished( sal_False ),
76 : pViewOptions( pViewOpt ),
77 : sAccessibleString(),
78 : aLineBreaks(),
79 : aModelPositions(),
80 : aAccessiblePositions(),
81 : pSentences( 0 ),
82 : nBeforePortions( 0 ),
83 28 : bLastIsSpecial( sal_False )
84 : {
85 : OSL_ENSURE( pTxtNode != NULL, "Text node is needed!" );
86 :
87 : // reserve some space to reduce memory allocations
88 28 : aLineBreaks.reserve( 5 );
89 28 : aModelPositions.reserve( 10 );
90 28 : aAccessiblePositions.reserve( 10 );
91 :
92 : // always include 'first' line-break position
93 28 : aLineBreaks.push_back( 0 );
94 28 : }
95 :
96 84 : SwAccessiblePortionData::~SwAccessiblePortionData()
97 : {
98 28 : delete pSentences;
99 56 : }
100 :
101 12 : void SwAccessiblePortionData::Text(sal_uInt16 nLength, sal_uInt16 nType, sal_Int32 /*nHeight*/, sal_Int32 /*nWidth*/)
102 : {
103 : OSL_ENSURE( (nModelPosition + nLength) <= pTxtNode->GetTxt().getLength(),
104 : "portion exceeds model string!" );
105 :
106 : OSL_ENSURE( !bFinished, "We are already done!" );
107 :
108 : // ignore zero-length portions
109 12 : if( nLength == 0 )
110 20 : return;
111 :
112 : // store 'old' positions
113 4 : aModelPositions.push_back( nModelPosition );
114 4 : aAccessiblePositions.push_back( aBuffer.getLength() );
115 :
116 : // store portion attributes
117 4 : sal_uInt8 nAttr = IsGrayPortionType(nType) ? PORATTR_GRAY : 0;
118 4 : aPortionAttrs.push_back( nAttr );
119 :
120 : // update buffer + nModelPosition
121 : aBuffer.append( OUString(
122 4 : pTxtNode->GetTxt().copy(nModelPosition, nLength)) );
123 4 : nModelPosition += nLength;
124 :
125 4 : bLastIsSpecial = sal_False;
126 : }
127 :
128 68 : void SwAccessiblePortionData::Special(
129 : sal_uInt16 nLength, const String& rText, sal_uInt16 nType, sal_Int32 /*nHeight*/, sal_Int32 /*nWidth*/)
130 : {
131 : OSL_ENSURE( nModelPosition >= 0, "illegal position" );
132 : OSL_ENSURE( (nModelPosition + nLength) <= pTxtNode->GetTxt().getLength(),
133 : "portion exceeds model string!" );
134 :
135 : OSL_ENSURE( !bFinished, "We are already done!" );
136 :
137 : // construct string with representation; either directly from
138 : // rText, or use resources for special case portions
139 68 : OUString sDisplay;
140 68 : switch( nType )
141 : {
142 : case POR_POSTITS:
143 : case POR_FLYCNT:
144 : case POR_GRFNUM:
145 0 : sDisplay = OUString(sal_Unicode(0xfffc));
146 :
147 0 : break;
148 : case POR_NUMBER:
149 : {
150 0 : sDisplay = OUString( rText ) + " ";
151 0 : break;
152 : }
153 : // #i111768# - apply patch from kstribley:
154 : // Include the control characters.
155 : case POR_CONTROLCHAR:
156 : {
157 0 : sDisplay = OUString( rText ) + OUString( pTxtNode->GetTxt()[nModelPosition] );
158 0 : break;
159 : }
160 : default:
161 68 : sDisplay = OUString( rText );
162 68 : break;
163 : }
164 :
165 : // ignore zero/zero portions (except for terminators)
166 68 : if( (nLength == 0) && (sDisplay.getLength() == 0) && (nType != POR_TERMINATE) )
167 70 : return;
168 :
169 : // special treatment for zero length portion at the beginning:
170 : // count as 'before' portion
171 66 : if( ( nLength == 0 ) && ( nModelPosition == 0 ) )
172 31 : nBeforePortions++;
173 :
174 : // store the 'old' positions
175 66 : aModelPositions.push_back( nModelPosition );
176 66 : aAccessiblePositions.push_back( aBuffer.getLength() );
177 :
178 : // store portion attributes
179 66 : sal_uInt8 nAttr = PORATTR_SPECIAL;
180 66 : if( IsGrayPortionType(nType) ) nAttr |= PORATTR_GRAY;
181 66 : if( nLength == 0 ) nAttr |= PORATTR_READONLY;
182 66 : if( nType == POR_TERMINATE ) nAttr |= PORATTR_TERM;
183 66 : aPortionAttrs.push_back( nAttr );
184 :
185 : // update buffer + nModelPosition
186 66 : aBuffer.append( sDisplay );
187 66 : nModelPosition += nLength;
188 :
189 : // remember 'last' special portion (unless it's our own 'closing'
190 : // portions from 'Finish()'
191 66 : if( nType != POR_TERMINATE )
192 10 : bLastIsSpecial = sal_True;
193 : }
194 :
195 78 : void SwAccessiblePortionData::LineBreak(KSHORT /*nWidth*/)
196 : {
197 : OSL_ENSURE( !bFinished, "We are already done!" );
198 :
199 78 : aLineBreaks.push_back( aBuffer.getLength() );
200 78 : }
201 :
202 0 : void SwAccessiblePortionData::Skip(sal_uInt16 nLength)
203 : {
204 : OSL_ENSURE( !bFinished, "We are already done!" );
205 : OSL_ENSURE( aModelPositions.empty(), "Never Skip() after portions" );
206 : OSL_ENSURE( nLength <= pTxtNode->GetTxt().getLength(),
207 : "skip exceeds model string!" );
208 :
209 0 : nModelPosition += nLength;
210 0 : }
211 :
212 28 : void SwAccessiblePortionData::Finish()
213 : {
214 : OSL_ENSURE( !bFinished, "We are already done!" );
215 :
216 : // include terminator values: always include two 'last character'
217 : // markers in the position arrays to make sure we always find one
218 : // position before the end
219 28 : Special( 0, String(), POR_TERMINATE );
220 28 : Special( 0, String(), POR_TERMINATE );
221 28 : LineBreak(0);
222 28 : LineBreak(0);
223 :
224 28 : sAccessibleString = aBuffer.makeStringAndClear();
225 28 : bFinished = sal_True;
226 28 : }
227 :
228 0 : sal_Bool SwAccessiblePortionData::IsPortionAttrSet(
229 : size_t nPortionNo, sal_uInt8 nAttr ) const
230 : {
231 : OSL_ENSURE( nPortionNo < aPortionAttrs.size(),
232 : "Illegal portion number" );
233 0 : return (aPortionAttrs[nPortionNo] & nAttr) != 0;
234 : }
235 :
236 0 : sal_Bool SwAccessiblePortionData::IsSpecialPortion( size_t nPortionNo ) const
237 : {
238 0 : return IsPortionAttrSet(nPortionNo, PORATTR_SPECIAL);
239 : }
240 :
241 0 : sal_Bool SwAccessiblePortionData::IsReadOnlyPortion( size_t nPortionNo ) const
242 : {
243 0 : return IsPortionAttrSet(nPortionNo, PORATTR_READONLY);
244 : }
245 :
246 70 : sal_Bool SwAccessiblePortionData::IsGrayPortionType( sal_uInt16 nType ) const
247 : {
248 : // gray portions?
249 : // Compare with: inftxt.cxx, SwTxtPaintInfo::DrawViewOpt(...)
250 70 : sal_Bool bGray = sal_False;
251 70 : switch( nType )
252 : {
253 : case POR_FTN:
254 : case POR_ISOREF:
255 : case POR_REF:
256 : case POR_QUOVADIS:
257 : case POR_NUMBER:
258 : case POR_FLD:
259 : case POR_URL:
260 : case POR_ISOTOX:
261 : case POR_TOX:
262 : case POR_HIDDEN:
263 18 : bGray = !pViewOptions->IsPagePreview() &&
264 18 : !pViewOptions->IsReadonly() && SwViewOption::IsFieldShadings();
265 9 : break;
266 0 : case POR_TAB: bGray = pViewOptions->IsTab(); break;
267 0 : case POR_SOFTHYPH: bGray = pViewOptions->IsSoftHyph(); break;
268 0 : case POR_BLANK: bGray = pViewOptions->IsHardBlank(); break;
269 : default:
270 61 : break; // bGray is false
271 : }
272 70 : return bGray;
273 : }
274 :
275 34 : const OUString& SwAccessiblePortionData::GetAccessibleString() const
276 : {
277 : OSL_ENSURE( bFinished, "Shouldn't call this before we are done!" );
278 :
279 34 : return sAccessibleString;
280 : }
281 :
282 :
283 0 : void SwAccessiblePortionData::GetLineBoundary(
284 : Boundary& rBound,
285 : sal_Int32 nPos ) const
286 : {
287 : FillBoundary( rBound, aLineBreaks,
288 0 : FindBreak( aLineBreaks, nPos ) );
289 0 : }
290 :
291 : // #i89175#
292 0 : sal_Int32 SwAccessiblePortionData::GetLineCount() const
293 : {
294 0 : size_t nBreaks = aLineBreaks.size();
295 : // A non-empty paragraph has at least 4 breaks: one for each line3 and
296 : // 3 additional ones.
297 : // An empty paragraph has 3 breaks.
298 : // Less than 3 breaks is an error case.
299 : sal_Int32 nLineCount = ( nBreaks > 3 )
300 0 : ? nBreaks - 3
301 0 : : ( ( nBreaks == 3 ) ? 1 : 0 );
302 0 : return nLineCount;
303 : }
304 :
305 0 : sal_Int32 SwAccessiblePortionData::GetLineNo( const sal_Int32 nPos ) const
306 : {
307 0 : sal_Int32 nLineNo = FindBreak( aLineBreaks, nPos );
308 :
309 : // handling of position after last character
310 0 : const sal_Int32 nLineCount( GetLineCount() );
311 0 : if ( nLineNo >= nLineCount )
312 : {
313 0 : nLineNo = nLineCount - 1;
314 : }
315 :
316 0 : return nLineNo;
317 : }
318 :
319 0 : void SwAccessiblePortionData::GetBoundaryOfLine( const sal_Int32 nLineNo,
320 : i18n::Boundary& rLineBound )
321 : {
322 0 : FillBoundary( rLineBound, aLineBreaks, nLineNo );
323 0 : }
324 :
325 0 : void SwAccessiblePortionData::GetLastLineBoundary(
326 : Boundary& rBound ) const
327 : {
328 : OSL_ENSURE( aLineBreaks.size() >= 2, "need min + max value" );
329 :
330 : // The last two positions except the two delimiters are the ones
331 : // we are looking for, except for empty paragraphs (nBreaks==3)
332 0 : size_t nBreaks = aLineBreaks.size();
333 0 : FillBoundary( rBound, aLineBreaks, nBreaks <= 3 ? 0 : nBreaks-4 );
334 0 : }
335 :
336 0 : sal_uInt16 SwAccessiblePortionData::GetModelPosition( sal_Int32 nPos ) const
337 : {
338 : OSL_ENSURE( nPos >= 0, "illegal position" );
339 : OSL_ENSURE( nPos <= sAccessibleString.getLength(), "illegal position" );
340 :
341 : // find the portion number
342 0 : size_t nPortionNo = FindBreak( aAccessiblePositions, nPos );
343 :
344 : // get model portion size
345 0 : sal_Int32 nStartPos = aModelPositions[nPortionNo];
346 :
347 : // if it's a non-special portion, move into the portion, else
348 : // return the portion start
349 0 : if( ! IsSpecialPortion( nPortionNo ) )
350 : {
351 : // 'wide' portions have to be of the same width
352 : OSL_ENSURE( ( aModelPositions[nPortionNo+1] - nStartPos ) ==
353 : ( aAccessiblePositions[nPortionNo+1] -
354 : aAccessiblePositions[nPortionNo] ),
355 : "accesability portion disagrees with text model" );
356 :
357 0 : sal_Int32 nWithinPortion = nPos - aAccessiblePositions[nPortionNo];
358 0 : nStartPos += nWithinPortion;
359 : }
360 : // else: return nStartPos unmodified
361 :
362 : OSL_ENSURE( (nStartPos >= 0) && (nStartPos < USHRT_MAX),
363 : "How can the SwTxtNode have so many characters?" );
364 0 : return static_cast<sal_uInt16>(nStartPos);
365 : }
366 :
367 0 : void SwAccessiblePortionData::FillBoundary(
368 : Boundary& rBound,
369 : const Positions_t& rPositions,
370 : size_t nPos ) const
371 : {
372 0 : rBound.startPos = rPositions[nPos];
373 0 : rBound.endPos = rPositions[nPos+1];
374 0 : }
375 :
376 :
377 66 : size_t SwAccessiblePortionData::FindBreak(
378 : const Positions_t& rPositions,
379 : sal_Int32 nValue ) const
380 : {
381 : OSL_ENSURE( rPositions.size() >= 2, "need min + max value" );
382 : OSL_ENSURE( rPositions[0] <= nValue, "need min value" );
383 : OSL_ENSURE( rPositions[rPositions.size()-1] >= nValue,
384 : "need first terminator value" );
385 : OSL_ENSURE( rPositions[rPositions.size()-2] >= nValue,
386 : "need second terminator value" );
387 :
388 66 : size_t nMin = 0;
389 66 : size_t nMax = rPositions.size()-2;
390 :
391 : // loop until no more than two candidates are left
392 132 : while( nMin+1 < nMax )
393 : {
394 : // check loop invariants
395 : OSL_ENSURE( ( (nMin == 0) && (rPositions[nMin] <= nValue) ) ||
396 : ( (nMin != 0) && (rPositions[nMin] < nValue) ),
397 : "minvalue not minimal" );
398 : OSL_ENSURE( nValue <= rPositions[nMax], "max value not maximal" );
399 :
400 : // get middle (and ensure progress)
401 0 : size_t nMiddle = (nMin + nMax)/2;
402 : OSL_ENSURE( nMin < nMiddle, "progress?" );
403 : OSL_ENSURE( nMiddle < nMax, "progress?" );
404 :
405 : // check array
406 : OSL_ENSURE( rPositions[nMin] <= rPositions[nMiddle],
407 : "garbled positions array" );
408 : OSL_ENSURE( rPositions[nMiddle] <= rPositions[nMax],
409 : "garbled positions array" );
410 :
411 0 : if( nValue > rPositions[nMiddle] )
412 0 : nMin = nMiddle;
413 : else
414 0 : nMax = nMiddle;
415 : }
416 :
417 : // only two are left; we only need to check which one is the winner
418 : OSL_ENSURE( (nMax == nMin) || (nMax == nMin+1), "only two left" );
419 66 : if( (rPositions[nMin] < nValue) && (rPositions[nMin+1] <= nValue) )
420 25 : nMin = nMin+1;
421 :
422 : // finally, check to see whether the returned value is the 'right' position
423 : OSL_ENSURE( rPositions[nMin] <= nValue, "not smaller or equal" );
424 : OSL_ENSURE( nValue <= rPositions[nMin+1], "not equal or larger" );
425 : OSL_ENSURE( (nMin == 0) || (rPositions[nMin-1] <= nValue),
426 : "earlier value should have been returned" );
427 :
428 : OSL_ENSURE( nMin < rPositions.size()-1,
429 : "shouldn't return last position (due to termintator values)" );
430 :
431 66 : return nMin;
432 : }
433 :
434 66 : size_t SwAccessiblePortionData::FindLastBreak(
435 : const Positions_t& rPositions,
436 : sal_Int32 nValue ) const
437 : {
438 66 : size_t nResult = FindBreak( rPositions, nValue );
439 :
440 : // skip 'zero-length' portions
441 : // #i70538# consider size of <rPosition> and ignore last entry
442 138 : while ( nResult < rPositions.size() - 2 &&
443 3 : rPositions[nResult+1] <= nValue )
444 : {
445 3 : nResult++;
446 : }
447 :
448 66 : return nResult;
449 : }
450 :
451 0 : void SwAccessiblePortionData::GetSentenceBoundary(
452 : Boundary& rBound,
453 : sal_Int32 nPos )
454 : {
455 : OSL_ENSURE( nPos >= 0, "illegal position; check before" );
456 : OSL_ENSURE( nPos < sAccessibleString.getLength(), "illegal position" );
457 :
458 0 : if( pSentences == NULL )
459 : {
460 : OSL_ENSURE( g_pBreakIt != NULL, "We always need a break." );
461 : OSL_ENSURE( g_pBreakIt->GetBreakIter().is(), "No break-iterator." );
462 0 : if( g_pBreakIt->GetBreakIter().is() )
463 : {
464 0 : pSentences = new Positions_t();
465 0 : pSentences->reserve(10);
466 :
467 : // use xBreak->endOfSentence to iterate over all words; store
468 : // positions in pSentences
469 0 : sal_Int32 nCurrent = 0;
470 0 : sal_Int32 nLength = sAccessibleString.getLength();
471 0 : do
472 : {
473 0 : pSentences->push_back( nCurrent );
474 :
475 0 : sal_uInt16 nModelPos = GetModelPosition( nCurrent );
476 :
477 0 : sal_Int32 nNew = g_pBreakIt->GetBreakIter()->endOfSentence(
478 : sAccessibleString, nCurrent,
479 0 : g_pBreakIt->GetLocale(pTxtNode->GetLang(nModelPos)) ) + 1;
480 :
481 0 : if( (nNew < 0) && (nNew > nLength) )
482 0 : nNew = nLength;
483 0 : else if (nNew <= nCurrent)
484 0 : nNew = nCurrent + 1; // ensure forward progress
485 :
486 0 : nCurrent = nNew;
487 : }
488 0 : while (nCurrent < nLength);
489 :
490 : // finish with two terminators
491 0 : pSentences->push_back( nLength );
492 0 : pSentences->push_back( nLength );
493 : }
494 : else
495 : {
496 : // no break iterator -> empty word
497 0 : rBound.startPos = 0;
498 0 : rBound.endPos = 0;
499 0 : return;
500 : }
501 : }
502 :
503 0 : FillBoundary( rBound, *pSentences, FindBreak( *pSentences, nPos ) );
504 : }
505 :
506 0 : void SwAccessiblePortionData::GetAttributeBoundary(
507 : Boundary& rBound,
508 : sal_Int32 nPos) const
509 : {
510 : OSL_ENSURE( pTxtNode != NULL, "Need SwTxtNode!" );
511 :
512 : // attribute boundaries can only occur on portion boundaries
513 : FillBoundary( rBound, aAccessiblePositions,
514 0 : FindBreak( aAccessiblePositions, nPos ) );
515 0 : }
516 :
517 66 : sal_Int32 SwAccessiblePortionData::GetAccessiblePosition( sal_uInt16 nPos ) const
518 : {
519 : OSL_ENSURE( nPos <= pTxtNode->GetTxt().getLength(), "illegal position" );
520 :
521 : // find the portion number
522 : // #i70538# - consider "empty" model portions - e.g. number portion
523 : size_t nPortionNo = FindLastBreak( aModelPositions,
524 66 : static_cast<sal_Int32>(nPos) );
525 :
526 66 : sal_Int32 nRet = aAccessiblePositions[nPortionNo];
527 :
528 : // if the model portion has more than one position, go into it;
529 : // else return that position
530 66 : sal_Int32 nStartPos = aModelPositions[nPortionNo];
531 66 : sal_Int32 nEndPos = aModelPositions[nPortionNo+1];
532 66 : if( (nEndPos - nStartPos) > 1 )
533 : {
534 : // 'wide' portions have to be of the same width
535 : OSL_ENSURE( ( nEndPos - nStartPos ) ==
536 : ( aAccessiblePositions[nPortionNo+1] -
537 : aAccessiblePositions[nPortionNo] ),
538 : "accesability portion disagrees with text model" );
539 :
540 0 : sal_Int32 nWithinPortion = nPos - aModelPositions[nPortionNo];
541 0 : nRet += nWithinPortion;
542 : }
543 : // else: return nRet unmodified
544 :
545 : OSL_ENSURE( (nRet >= 0) && (nRet <= sAccessibleString.getLength()),
546 : "too long!" );
547 66 : return nRet;
548 : }
549 :
550 0 : sal_uInt16 SwAccessiblePortionData::FillSpecialPos(
551 : sal_Int32 nPos,
552 : SwSpecialPos& rPos,
553 : SwSpecialPos*& rpPos ) const
554 : {
555 0 : size_t nPortionNo = FindLastBreak( aAccessiblePositions, nPos );
556 :
557 0 : sal_uInt8 nExtend(SP_EXTEND_RANGE_NONE);
558 0 : sal_Int32 nRefPos(0);
559 0 : sal_Int32 nModelPos(0);
560 :
561 0 : if( nPortionNo < nBeforePortions )
562 : {
563 0 : nExtend = SP_EXTEND_RANGE_BEFORE;
564 0 : rpPos = &rPos;
565 : }
566 : else
567 : {
568 0 : sal_Int32 nModelEndPos = aModelPositions[nPortionNo+1];
569 0 : nModelPos = aModelPositions[nPortionNo];
570 :
571 : // skip backwards over zero-length portions, since GetCharRect()
572 : // counts all model-zero-length portions as belonging to the
573 : // previus portion
574 0 : size_t nCorePortionNo = nPortionNo;
575 0 : while( nModelPos == nModelEndPos )
576 : {
577 0 : nCorePortionNo--;
578 0 : nModelEndPos = nModelPos;
579 0 : nModelPos = aModelPositions[nCorePortionNo];
580 :
581 : OSL_ENSURE( nModelPos >= 0, "Can't happen." );
582 : OSL_ENSURE( nCorePortionNo >= nBeforePortions, "Can't happen." );
583 : }
584 : OSL_ENSURE( nModelPos != nModelEndPos,
585 : "portion with core-representation expected" );
586 :
587 : // if we have anything except plain text, compute nExtend + nRefPos
588 0 : if( (nModelEndPos - nModelPos == 1) &&
589 0 : (pTxtNode->GetTxt()[nModelPos] != sAccessibleString.getStr()[nPos]))
590 : {
591 : // case 1: a one-character, non-text portion
592 : // reference position is the first accessibilty for our
593 : // core portion
594 0 : nRefPos = aAccessiblePositions[ nCorePortionNo ];
595 0 : nExtend = SP_EXTEND_RANGE_NONE;
596 0 : rpPos = &rPos;
597 : }
598 0 : else if(nPortionNo != nCorePortionNo)
599 : {
600 : // case 2: a multi-character (text!) portion, followed by
601 : // zero-length portions
602 : // reference position is the first character of the next
603 : // portion, and we are 'behind'
604 0 : nRefPos = aAccessiblePositions[ nCorePortionNo+1 ];
605 0 : nExtend = SP_EXTEND_RANGE_BEHIND;
606 0 : rpPos = &rPos;
607 : }
608 : else
609 : {
610 : // case 3: regular text portion
611 : OSL_ENSURE( ( nModelEndPos - nModelPos ) ==
612 : ( aAccessiblePositions[nPortionNo+1] -
613 : aAccessiblePositions[nPortionNo] ),
614 : "text portion expected" );
615 :
616 0 : nModelPos += nPos - aAccessiblePositions[ nPortionNo ];
617 0 : rpPos = NULL;
618 : }
619 : }
620 0 : if( rpPos != NULL )
621 : {
622 : OSL_ENSURE( rpPos == &rPos, "Yes!" );
623 : OSL_ENSURE( nRefPos <= nPos, "wrong reference" );
624 : OSL_ENSURE( (nExtend == SP_EXTEND_RANGE_NONE) ||
625 : (nExtend == SP_EXTEND_RANGE_BEFORE) ||
626 : (nExtend == SP_EXTEND_RANGE_BEHIND), "need extend" );
627 :
628 : // get the line number, and adjust nRefPos for the line
629 : // (if necessary)
630 0 : size_t nRefLine = FindBreak( aLineBreaks, nRefPos );
631 0 : size_t nMyLine = FindBreak( aLineBreaks, nPos );
632 0 : sal_uInt16 nLineOffset = static_cast<sal_uInt16>( nMyLine - nRefLine );
633 0 : if( nLineOffset != 0 )
634 0 : nRefPos = aLineBreaks[ nMyLine ];
635 :
636 : // fill char offset and 'special position'
637 0 : rPos.nCharOfst = static_cast<sal_uInt16>( nPos - nRefPos );
638 0 : rPos.nExtendRange = nExtend;
639 0 : rPos.nLineOfst = nLineOffset;
640 : }
641 :
642 0 : return static_cast<sal_uInt16>( nModelPos );
643 : }
644 :
645 0 : void SwAccessiblePortionData::AdjustAndCheck(
646 : sal_Int32 nPos,
647 : size_t& nPortionNo,
648 : sal_uInt16& nCorePos,
649 : sal_Bool& bEdit) const
650 : {
651 : // find portion and get mode position
652 0 : nPortionNo = FindBreak( aAccessiblePositions, nPos );
653 0 : nCorePos = static_cast<sal_uInt16>( aModelPositions[ nPortionNo ] );
654 :
655 : // for special portions, make sure we're on a portion boundary
656 : // for text portions, add the in-portion offset
657 0 : if( IsSpecialPortion( nPortionNo ) )
658 0 : bEdit &= nPos == aAccessiblePositions[nPortionNo];
659 : else
660 : nCorePos = static_cast<sal_uInt16>( nCorePos +
661 0 : nPos - aAccessiblePositions[nPortionNo] );
662 0 : }
663 :
664 0 : sal_Bool SwAccessiblePortionData::GetEditableRange(
665 : sal_Int32 nStart, sal_Int32 nEnd,
666 : sal_uInt16& nCoreStart, sal_uInt16& nCoreEnd ) const
667 : {
668 0 : sal_Bool bIsEditable = sal_True;
669 :
670 : // get start and end portions
671 : size_t nStartPortion, nEndPortion;
672 0 : AdjustAndCheck( nStart, nStartPortion, nCoreStart, bIsEditable );
673 0 : AdjustAndCheck( nEnd, nEndPortion, nCoreEnd, bIsEditable );
674 :
675 : // iterate over portions, and make sure there is no read-only portion
676 : // in-between
677 0 : size_t nLastPortion = nEndPortion;
678 :
679 : // don't count last portion if we're in front of a special portion
680 0 : if( IsSpecialPortion(nLastPortion) )
681 : {
682 0 : if (nLastPortion > 0)
683 0 : nLastPortion--;
684 : else
685 : // special case: because size_t is usually unsigned, we can't just
686 : // decrease nLastPortion to -1 (which would normally do the job, so
687 : // this whole if wouldn't be needed). Instead, we'll do this
688 : // special case and just increae the start portion beyond the last
689 : // portion to make sure the loop below will have zero iteration.
690 0 : nStartPortion = nLastPortion + 1;
691 : }
692 :
693 0 : for( size_t nPor = nStartPortion; nPor <= nLastPortion; nPor ++ )
694 : {
695 0 : bIsEditable &= ! IsReadOnlyPortion( nPor );
696 : }
697 :
698 0 : return bIsEditable;
699 : }
700 :
701 66 : sal_Bool SwAccessiblePortionData::IsValidCorePosition( sal_uInt16 nPos ) const
702 : {
703 : // a position is valid its within the model positions that we know
704 132 : return ( aModelPositions[0] <= nPos ) &&
705 132 : ( nPos <= aModelPositions[ aModelPositions.size()-1 ] );
706 : }
707 :
708 0 : sal_uInt16 SwAccessiblePortionData::GetFirstValidCorePosition() const
709 : {
710 0 : return static_cast<sal_uInt16>( aModelPositions[0] );
711 : }
712 :
713 0 : sal_uInt16 SwAccessiblePortionData::GetLastValidCorePosition() const
714 : {
715 0 : return static_cast<sal_uInt16>( aModelPositions[ aModelPositions.size()-1 ] );
716 99 : }
717 :
718 : /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
|