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