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