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