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