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