LibreOffice Module editeng (master) 1
AccessibleStaticTextBase.cxx
Go to the documentation of this file.
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
21// Global header
22
23
24#include <utility>
25#include <memory>
26#include <vector>
27#include <algorithm>
28#include <rtl/ustrbuf.hxx>
29#include <tools/debug.hxx>
30#include <vcl/svapp.hxx>
32#include <com/sun/star/lang/IndexOutOfBoundsException.hpp>
33#include <com/sun/star/uno/Reference.hxx>
34#include <com/sun/star/awt/Point.hpp>
35#include <com/sun/star/awt/Rectangle.hpp>
36#include <com/sun/star/accessibility/AccessibleTextType.hpp>
37
38
39// Project-local header
40
41
42#include <editeng/editdata.hxx>
43#include <editeng/unoedprx.hxx>
46
47
48using namespace ::com::sun::star;
49using namespace ::com::sun::star::accessibility;
50
51/* TODO:
52 =====
53
54 - separate adapter functionality from AccessibleStaticText class
55
56 - refactor common loops into templates, using mem_fun
57
58 */
59
60namespace accessibility
61{
62 typedef std::vector< beans::PropertyValue > PropertyValueVector;
63
64 namespace {
65
66 class PropertyValueEqualFunctor
67 {
68 const beans::PropertyValue& m_rPValue;
69
70 public:
71 explicit PropertyValueEqualFunctor(const beans::PropertyValue& rPValue)
72 : m_rPValue(rPValue)
73 {}
74 bool operator() ( const beans::PropertyValue& rhs ) const
75 {
76 return ( m_rPValue.Name == rhs.Name && m_rPValue.Value == rhs.Value );
77 }
78 };
79
80 }
81
82 sal_Unicode const cNewLine(0x0a);
83
84
85 // Static Helper
86
87
88 static ESelection MakeSelection( sal_Int32 nStartPara, sal_Int32 nStartIndex,
89 sal_Int32 nEndPara, sal_Int32 nEndIndex )
90 {
91 DBG_ASSERT(nStartPara >= 0 &&
92 nStartIndex >= 0 &&
93 nEndPara >= 0 &&
94 nEndIndex >= 0,
95 "AccessibleStaticTextBase_Impl::MakeSelection: index value overflow");
96
97 return ESelection(nStartPara, nStartIndex, nEndPara, nEndIndex);
98 }
99
100
101 // AccessibleStaticTextBase_Impl declaration
102
103
113 {
115 public:
116
117 // receive pointer to our frontend class and view window
119
120 void SetEditSource( std::unique_ptr< SvxEditSource > && pEditSource );
121
122 void SetEventSource( const uno::Reference< XAccessible >& rInterface )
123 {
124
125 mxThis = rInterface;
126 }
127
128 void SetOffset( const Point& );
129
130 void Dispose();
131
132 AccessibleEditableTextPara& GetParagraph( sal_Int32 nPara ) const;
133 sal_Int32 GetParagraphCount() const;
134
135 EPosition Index2Internal( sal_Int32 nFlatIndex ) const
136 {
137
138 return ImpCalcInternal( nFlatIndex, false );
139 }
140
141 EPosition Range2Internal( sal_Int32 nFlatIndex ) const
142 {
143
144 return ImpCalcInternal( nFlatIndex, true );
145 }
146
147 sal_Int32 Internal2Index( EPosition nEEIndex ) const;
148
149 void CorrectTextSegment( TextSegment& aTextSegment,
150 int nPara ) const;
151
152 bool SetSelection( sal_Int32 nStartPara, sal_Int32 nStartIndex,
153 sal_Int32 nEndPara, sal_Int32 nEndIndex );
154 bool CopyText( sal_Int32 nStartPara, sal_Int32 nStartIndex,
155 sal_Int32 nEndPara, sal_Int32 nEndIndex );
156
158 bool RemoveLineBreakCount( sal_Int32& rIndex );
159
160 private:
161
162 EPosition ImpCalcInternal( sal_Int32 nFlatIndex, bool bExclusive ) const;
163
164 // our frontend class (the one implementing the actual
165 // interface). That's not necessarily the one containing the impl
166 // pointer
167 uno::Reference< XAccessible > mxThis;
168
169 // implements our functionality, we're just an adapter (guarded by solar mutex)
171
172 // a wrapper for the text forwarders (guarded by solar mutex)
173 mutable SvxEditSourceAdapter maEditSource;
174 };
175
176
177 // AccessibleStaticTextBase_Impl implementation
178
179
181 mxTextParagraph( new AccessibleEditableTextPara(nullptr) )
182 {
183
184 // TODO: this is still somewhat of a hack, all the more since
185 // now the maTextParagraph has an empty parent reference set
186 }
187
188 void AccessibleStaticTextBase_Impl::SetEditSource( std::unique_ptr< SvxEditSource > && pEditSource )
189 {
190
191 maEditSource.SetEditSource( std::move(pEditSource) );
192 if( mxTextParagraph.is() )
193 mxTextParagraph->SetEditSource( &maEditSource );
194 }
195
197 {
198 if( mxTextParagraph.is() )
199 mxTextParagraph->SetEEOffset( rPoint );
200 }
201
203 {
204
205 // we're the owner of the paragraph, so destroy it, too
206 if( mxTextParagraph.is() )
207 mxTextParagraph->Dispose();
208
209 // drop references
210 mxThis = nullptr;
211 mxTextParagraph.clear();
212 }
213
214 AccessibleEditableTextPara& AccessibleStaticTextBase_Impl::GetParagraph( sal_Int32 nPara ) const
215 {
216
217 if( !mxTextParagraph.is() )
218 throw lang::DisposedException ("object has been already disposed", mxThis );
219
220 // TODO: Have a different method on AccessibleEditableTextPara
221 // that does not care about state changes
222 mxTextParagraph->SetParagraphIndex( nPara );
223
224 return *mxTextParagraph;
225 }
226
228 {
229
230 if( !mxTextParagraph.is() )
231 return 0;
232 else
233 return mxTextParagraph->GetTextForwarder().GetParagraphCount();
234 }
235
237 {
238 // XXX checks for overflow and returns maximum if so
239 sal_Int32 aRes(0);
240 for(sal_Int32 i=0; i<nEEIndex.nPara; ++i)
241 {
242 sal_Int32 nCount = GetParagraph(i).getCharacterCount();
243 if (SAL_MAX_INT32 - aRes > nCount)
244 return SAL_MAX_INT32;
245 aRes += nCount;
246 }
247
248 if (SAL_MAX_INT32 - aRes > nEEIndex.nIndex)
249 return SAL_MAX_INT32;
250 return aRes + nEEIndex.nIndex;
251 }
252
254 int nPara ) const
255 {
256 // Keep 'invalid' values at the TextSegment
257 if( aTextSegment.SegmentStart != -1 &&
258 aTextSegment.SegmentEnd != -1 )
259 {
260 // #112814# Correct TextSegment by paragraph offset
261 sal_Int32 nOffset(0);
262 int i;
263 for(i=0; i<nPara; ++i)
264 nOffset += GetParagraph(i).getCharacterCount();
265
266 aTextSegment.SegmentStart += nOffset;
267 aTextSegment.SegmentEnd += nOffset;
268 }
269 }
270
271 EPosition AccessibleStaticTextBase_Impl::ImpCalcInternal( sal_Int32 nFlatIndex, bool bExclusive ) const
272 {
273
274 if( nFlatIndex < 0 )
275 throw lang::IndexOutOfBoundsException("AccessibleStaticTextBase_Impl::Index2Internal: character index out of bounds",
276 mxThis);
277 // gratuitously accepting larger indices here, AccessibleEditableTextPara will throw eventually
278
279 sal_Int32 nCurrPara, nCurrIndex, nParas, nCurrCount;
280 for( nCurrPara=0, nParas=GetParagraphCount(), nCurrCount=0, nCurrIndex=0; nCurrPara<nParas; ++nCurrPara )
281 {
282 nCurrCount = GetParagraph( nCurrPara ).getCharacterCount();
283 nCurrIndex += nCurrCount;
284 if( nCurrIndex >= nFlatIndex )
285 {
286 // check overflow
287 DBG_ASSERT(nCurrPara >= 0 &&
288 nFlatIndex - nCurrIndex + nCurrCount >= 0,
289 "AccessibleStaticTextBase_Impl::Index2Internal: index value overflow");
290
291 return EPosition(nCurrPara, nFlatIndex - nCurrIndex + nCurrCount);
292 }
293 }
294
295 // #102170# Allow one-past the end for ranges
296 if( bExclusive && nCurrIndex == nFlatIndex )
297 {
298 // check overflow
299 DBG_ASSERT(nCurrPara > 0 &&
300 nFlatIndex - nCurrIndex + nCurrCount >= 0,
301 "AccessibleStaticTextBase_Impl::Index2Internal: index value overflow");
302
303 return EPosition(nCurrPara-1, nFlatIndex - nCurrIndex + nCurrCount);
304 }
305
306 // not found? Out of bounds
307 throw lang::IndexOutOfBoundsException("AccessibleStaticTextBase_Impl::Index2Internal: character index out of bounds",
308 mxThis);
309 }
310
311 bool AccessibleStaticTextBase_Impl::SetSelection( sal_Int32 nStartPara, sal_Int32 nStartIndex,
312 sal_Int32 nEndPara, sal_Int32 nEndIndex )
313 {
314
315 if( !mxTextParagraph.is() )
316 return false;
317
318 try
319 {
320 SvxEditViewForwarder& rCacheVF = mxTextParagraph->GetEditViewForwarder( true );
321 return rCacheVF.SetSelection( MakeSelection(nStartPara, nStartIndex, nEndPara, nEndIndex) );
322 }
323 catch( const uno::RuntimeException& )
324 {
325 return false;
326 }
327 }
328
329 bool AccessibleStaticTextBase_Impl::CopyText( sal_Int32 nStartPara, sal_Int32 nStartIndex,
330 sal_Int32 nEndPara, sal_Int32 nEndIndex )
331 {
332
333 if( !mxTextParagraph.is() )
334 return false;
335
336 try
337 {
338 SvxEditViewForwarder& rCacheVF = mxTextParagraph->GetEditViewForwarder( true );
339 mxTextParagraph->GetTextForwarder(); // MUST be after GetEditViewForwarder(), see method docs
340 bool aRetVal;
341
342 // save current selection
343 ESelection aOldSelection;
344
345 rCacheVF.GetSelection( aOldSelection );
346 rCacheVF.SetSelection( MakeSelection(nStartPara, nStartIndex, nEndPara, nEndIndex) );
347 aRetVal = rCacheVF.Copy();
348 rCacheVF.SetSelection( aOldSelection ); // restore
349
350 return aRetVal;
351 }
352 catch( const uno::RuntimeException& )
353 {
354 return false;
355 }
356 }
357
359 {
360 tools::Rectangle aRect;
361 if( mxTextParagraph.is() )
362 {
363 awt::Rectangle aAwtRect = mxTextParagraph->getBounds();
364 aRect = tools::Rectangle( Point( aAwtRect.X, aAwtRect.Y ), Size( aAwtRect.Width, aAwtRect.Height ) );
365 }
366 else
367 {
368 aRect.SetEmpty();
369 }
370 return aRect;
371 }
372 //the input argument is the index(including "\n" ) in the string.
373 //the function will calculate the actual index(not including "\n") in the string.
374 //and return true if the index is just at a "\n"
376 {
377 // get the total char number inside the cell.
378 sal_Int32 i, nCount, nParas;
379 for( i=0, nCount=0, nParas=GetParagraphCount(); i<nParas; ++i )
380 nCount += GetParagraph(i).getCharacterCount();
381 nCount = nCount + (nParas-1);
382 if( nCount == 0 && rIndex == 0) return false;
383
384
385 sal_Int32 nCurrPara, nCurrCount;
386 sal_Int32 nLineBreakPos = 0, nLineBreakCount = 0;
387 sal_Int32 nParaCount = GetParagraphCount();
388 for ( nCurrCount = 0, nCurrPara = 0; nCurrPara < nParaCount; nCurrPara++ )
389 {
390 nCurrCount += GetParagraph( nCurrPara ).getCharacterCount();
391 nLineBreakPos = nCurrCount++;
392 if ( rIndex == nLineBreakPos )
393 {
394 rIndex -= (++nLineBreakCount);//(++nLineBreakCount);
395 if ( rIndex < 0)
396 {
397 rIndex = 0;
398 }
399 //if the index is at the last position of the last paragraph
400 //there is no "\n" , so we should increase rIndex by 1 and return false.
401 if ( (nCurrPara+1) == nParaCount )
402 {
403 rIndex++;
404 return false;
405 }
406 else
407 {
408 return true;
409 }
410 }
411 else if ( rIndex < nLineBreakPos )
412 {
413 rIndex -= nLineBreakCount;
414 return false;
415 }
416 else
417 {
418 nLineBreakCount++;
419 }
420 }
421 return false;
422 }
423
424
425 // AccessibleStaticTextBase implementation
426
427 AccessibleStaticTextBase::AccessibleStaticTextBase( std::unique_ptr< SvxEditSource > && pEditSource ) :
428 mpImpl( new AccessibleStaticTextBase_Impl() )
429 {
430 SolarMutexGuard aGuard;
431
432 SetEditSource( std::move(pEditSource) );
433 }
434
436 {
437 }
438
439 void AccessibleStaticTextBase::SetEditSource( std::unique_ptr< SvxEditSource > && pEditSource )
440 {
441 // precondition: solar mutex locked
443
444 mpImpl->SetEditSource( std::move(pEditSource) );
445 }
446
447 void AccessibleStaticTextBase::SetEventSource( const uno::Reference< XAccessible >& rInterface )
448 {
449 mpImpl->SetEventSource( rInterface );
450
451 }
452
454 {
455 // precondition: solar mutex locked
457
458 mpImpl->SetOffset( rPoint );
459 }
460
462 {
463 mpImpl->Dispose();
464
465 }
466
467 // XAccessibleContext
469 {
470 // no children at all
471 return 0;
472 }
473
474 uno::Reference< XAccessible > AccessibleStaticTextBase::getAccessibleChild( sal_Int64 /*i*/ )
475 {
476 // no children at all
477 return uno::Reference< XAccessible >();
478 }
479
480 uno::Reference< XAccessible > AccessibleStaticTextBase::getAccessibleAtPoint( const awt::Point& /*_aPoint*/ )
481 {
482 // no children at all
483 return uno::Reference< XAccessible >();
484 }
485
486 // XAccessibleText
488 {
489 SolarMutexGuard aGuard;
490
491 sal_Int32 i, nPos, nParas;
492 for( i=0, nPos=-1, nParas=mpImpl->GetParagraphCount(); i<nParas; ++i )
493 {
494 if( (nPos=mpImpl->GetParagraph(i).getCaretPosition()) != -1 )
495 return nPos;
496 }
497
498 return nPos;
499 }
500
502 {
503 return setSelection(nIndex, nIndex);
504 }
505
507 {
508 SolarMutexGuard aGuard;
509
510 EPosition aPos( mpImpl->Index2Internal(nIndex) );
511
512 return mpImpl->GetParagraph( aPos.nPara ).getCharacter( aPos.nIndex );
513 }
514
515 uno::Sequence< beans::PropertyValue > SAL_CALL AccessibleStaticTextBase::getCharacterAttributes( sal_Int32 nIndex, const css::uno::Sequence< OUString >& aRequestedAttributes )
516 {
517 SolarMutexGuard aGuard;
518
519 //get the actual index without "\n"
520 mpImpl->RemoveLineBreakCount( nIndex );
521
522 EPosition aPos( mpImpl->Index2Internal(nIndex) );
523
524 return mpImpl->GetParagraph( aPos.nPara ).getCharacterAttributes( aPos.nIndex, aRequestedAttributes );
525 }
526
527 awt::Rectangle SAL_CALL AccessibleStaticTextBase::getCharacterBounds( sal_Int32 nIndex )
528 {
529 SolarMutexGuard aGuard;
530
531 // #108900# Allow ranges for nIndex, as one-past-the-end
532 // values are now legal, too.
533 EPosition aPos( mpImpl->Range2Internal(nIndex) );
534
535 // #i70916# Text in spread sheet cells return the wrong extents
536 AccessibleEditableTextPara& rPara = mpImpl->GetParagraph( aPos.nPara );
537 awt::Rectangle aParaBounds( rPara.getBounds() );
538 awt::Rectangle aBounds( rPara.getCharacterBounds( aPos.nIndex ) );
539 aBounds.X += aParaBounds.X;
540 aBounds.Y += aParaBounds.Y;
541
542 return aBounds;
543 }
544
546 {
547 SolarMutexGuard aGuard;
548
549 sal_Int32 i, nCount, nParas;
550 for( i=0, nCount=0, nParas=mpImpl->GetParagraphCount(); i<nParas; ++i )
551 nCount += mpImpl->GetParagraph(i).getCharacterCount();
552 //count on the number of "\n" which equals number of paragraphs decrease 1.
553 nCount = nCount + (nParas-1);
554 return nCount;
555 }
556
557 sal_Int32 SAL_CALL AccessibleStaticTextBase::getIndexAtPoint( const awt::Point& rPoint )
558 {
559 SolarMutexGuard aGuard;
560
561 const sal_Int32 nParas( mpImpl->GetParagraphCount() );
562 sal_Int32 nIndex;
563 int i;
564 for( i=0; i<nParas; ++i )
565 {
566 // TODO: maybe exploit the fact that paragraphs are
567 // ordered vertically for early exit
568
569 // #i70916# Text in spread sheet cells return the wrong extents
570 AccessibleEditableTextPara& rPara = mpImpl->GetParagraph( i );
571 awt::Rectangle aParaBounds( rPara.getBounds() );
572 awt::Point aPoint( rPoint );
573 aPoint.X -= aParaBounds.X;
574 aPoint.Y -= aParaBounds.Y;
575
576 // #112814# Use correct index offset
577 if ( ( nIndex = rPara.getIndexAtPoint( aPoint ) ) != -1 )
578 return mpImpl->Internal2Index(EPosition(i, nIndex));
579 }
580
581 return -1;
582 }
583
585 {
586 SolarMutexGuard aGuard;
587
588 sal_Int32 nStart( getSelectionStart() );
589 sal_Int32 nEnd( getSelectionEnd() );
590
591 // #104481# Return the empty string for 'no selection'
592 if( nStart < 0 || nEnd < 0 )
593 return OUString();
594
595 return getTextRange( nStart, nEnd );
596 }
597
599 {
600 SolarMutexGuard aGuard;
601
602 sal_Int32 i, nPos, nParas;
603 for( i=0, nPos=-1, nParas=mpImpl->GetParagraphCount(); i<nParas; ++i )
604 {
605 if( (nPos=mpImpl->GetParagraph(i).getSelectionStart()) != -1 )
606 return nPos;
607 }
608
609 return nPos;
610 }
611
613 {
614 SolarMutexGuard aGuard;
615
616 sal_Int32 i, nPos, nParas;
617 for( i=0, nPos=-1, nParas=mpImpl->GetParagraphCount(); i<nParas; ++i )
618 {
619 if( (nPos=mpImpl->GetParagraph(i).getSelectionEnd()) != -1 )
620 return nPos;
621 }
622
623 return nPos;
624 }
625
626 sal_Bool SAL_CALL AccessibleStaticTextBase::setSelection( sal_Int32 nStartIndex, sal_Int32 nEndIndex )
627 {
628 SolarMutexGuard aGuard;
629
630 EPosition aStartIndex( mpImpl->Range2Internal(nStartIndex) );
631 EPosition aEndIndex( mpImpl->Range2Internal(nEndIndex) );
632
633 return mpImpl->SetSelection( aStartIndex.nPara, aStartIndex.nIndex,
634 aEndIndex.nPara, aEndIndex.nIndex );
635 }
636
638 {
639 SolarMutexGuard aGuard;
640
641 sal_Int32 i, nParas;
642 OUStringBuffer aRes;
643 for( i=0, nParas=mpImpl->GetParagraphCount(); i<nParas; ++i )
644 aRes.append(mpImpl->GetParagraph(i).getText());
645
646 return aRes.makeStringAndClear();
647 }
648
649 OUString SAL_CALL AccessibleStaticTextBase::getTextRange( sal_Int32 nStartIndex, sal_Int32 nEndIndex )
650 {
651 SolarMutexGuard aGuard;
652
653 if( nStartIndex > nEndIndex )
654 std::swap(nStartIndex, nEndIndex);
655 //if startindex equals endindex we will get nothing. So return an empty string directly.
656 if ( nStartIndex == nEndIndex )
657 {
658 return OUString();
659 }
660 bool bStart = mpImpl->RemoveLineBreakCount( nStartIndex );
661 //if the start index is just at a "\n", we need to begin from the next char
662 if ( bStart )
663 {
664 nStartIndex++;
665 }
666 //we need to find out whether the previous position of the current endindex is at "\n" or not
667 //if yes we need to mark it and add "\n" at the end of the result
668 sal_Int32 nTemp = nEndIndex - 1;
669 bool bEnd = mpImpl->RemoveLineBreakCount( nTemp );
670 bool bTemp = mpImpl->RemoveLineBreakCount( nEndIndex );
671 //if the below condition is true it indicates an empty paragraph with just a "\n"
672 //so we need to set one "\n" flag to avoid duplication.
673 if ( bStart && bEnd && ( nStartIndex == nEndIndex) )
674 {
675 bEnd = false;
676 }
677 //if the current endindex is at a "\n", we need to increase endindex by 1 to make sure
678 //the char before "\n" is included. Because string returned by this function will not include
679 //the char at the endindex.
680 if ( bTemp )
681 {
682 nEndIndex++;
683 }
684 OUStringBuffer aRes;
685 EPosition aStartIndex( mpImpl->Range2Internal(nStartIndex) );
686 EPosition aEndIndex( mpImpl->Range2Internal(nEndIndex) );
687
688 // #102170# Special case: start and end paragraph are identical
689 if( aStartIndex.nPara == aEndIndex.nPara )
690 {
691 //we don't return the string directly now for that we have to do some further process for "\n"
692 aRes = mpImpl->GetParagraph( aStartIndex.nPara ).getTextRange( aStartIndex.nIndex, aEndIndex.nIndex );
693 }
694 else
695 {
696 sal_Int32 i( aStartIndex.nPara );
697 aRes = mpImpl->GetParagraph(i).getTextRange( aStartIndex.nIndex,
698 mpImpl->GetParagraph(i).getCharacterCount()/*-1*/);
699 ++i;
700
701 // paragraphs inbetween are fully included
702 for( ; i<aEndIndex.nPara; ++i )
703 {
704 aRes.append(OUStringChar(cNewLine) + mpImpl->GetParagraph(i).getText());
705 }
706
707 if( i<=aEndIndex.nPara )
708 {
709 //if the below condition is matched it means that endindex is at mid of the last paragraph
710 //we need to add a "\n" before we add the last part of the string.
711 if ( !bEnd && aEndIndex.nIndex )
712 {
713 aRes.append(cNewLine);
714 }
715 aRes.append(mpImpl->GetParagraph(i).getTextRange( 0, aEndIndex.nIndex ));
716 }
717 }
718 //According to the flag we marked before, we have to add "\n" at the beginning
719 //or at the end of the result string.
720 if ( bStart )
721 {
722 aRes.insert(0, OUStringChar(cNewLine));
723 }
724 if ( bEnd )
725 {
726 aRes.append(OUStringChar(cNewLine));
727 }
728 return aRes.makeStringAndClear();
729 }
730
731 css::accessibility::TextSegment SAL_CALL AccessibleStaticTextBase::getTextAtIndex( sal_Int32 nIndex, sal_Int16 aTextType )
732 {
733 SolarMutexGuard aGuard;
734
735 bool bLineBreak = mpImpl->RemoveLineBreakCount( nIndex );
736 EPosition aPos( mpImpl->Range2Internal(nIndex) );
737
738 css::accessibility::TextSegment aResult;
739
740 if( AccessibleTextType::PARAGRAPH == aTextType )
741 {
742 // #106393# Special casing one behind last paragraph is
743 // not necessary, since then, we return the content and
744 // boundary of that last paragraph. Range2Internal is
745 // tolerant against that, and returns the last paragraph
746 // in aPos.nPara.
747
748 // retrieve full text of the paragraph
749 aResult.SegmentText = mpImpl->GetParagraph( aPos.nPara ).getText();
750
751 // #112814# Adapt the start index with the paragraph offset
752 aResult.SegmentStart = mpImpl->Internal2Index( EPosition( aPos.nPara, 0 ) );
753 aResult.SegmentEnd = aResult.SegmentStart + aResult.SegmentText.getLength();
754 }
755 else if ( AccessibleTextType::ATTRIBUTE_RUN == aTextType )
756 {
757 SvxAccessibleTextAdapter& rTextForwarder = mpImpl->GetParagraph( aPos.nIndex ).GetTextForwarder();
758 sal_Int32 nStartIndex, nEndIndex;
759 if ( rTextForwarder.GetAttributeRun( nStartIndex, nEndIndex, aPos.nPara, aPos.nIndex, true ) )
760 {
761 aResult.SegmentText = getTextRange( nStartIndex, nEndIndex );
762 aResult.SegmentStart = nStartIndex;
763 aResult.SegmentEnd = nEndIndex;
764 }
765 }
766 else
767 {
768 // No special handling required, forward to wrapped class
769 aResult = mpImpl->GetParagraph( aPos.nPara ).getTextAtIndex( aPos.nIndex, aTextType );
770
771 // #112814# Adapt the start index with the paragraph offset
772 mpImpl->CorrectTextSegment( aResult, aPos.nPara );
773 if ( bLineBreak )
774 {
775 aResult.SegmentText = OUString(cNewLine);
776 }
777 }
778
779 return aResult;
780 }
781
782 css::accessibility::TextSegment SAL_CALL AccessibleStaticTextBase::getTextBeforeIndex( sal_Int32 nIndex, sal_Int16 aTextType )
783 {
784 SolarMutexGuard aGuard;
785
786 sal_Int32 nOldIdx = nIndex;
787 bool bLineBreak = mpImpl->RemoveLineBreakCount( nIndex );
788 EPosition aPos( mpImpl->Range2Internal(nIndex) );
789
790 css::accessibility::TextSegment aResult;
791
792 if( AccessibleTextType::PARAGRAPH == aTextType )
793 {
794 if( aPos.nIndex == mpImpl->GetParagraph( aPos.nPara ).getCharacterCount() )
795 {
796 // #103589# Special casing one behind the last paragraph
797 aResult.SegmentText = mpImpl->GetParagraph( aPos.nPara ).getText();
798
799 // #112814# Adapt the start index with the paragraph offset
800 aResult.SegmentStart = mpImpl->Internal2Index( EPosition( aPos.nPara, 0 ) );
801 }
802 else if( aPos.nPara > 0 )
803 {
804 aResult.SegmentText = mpImpl->GetParagraph( aPos.nPara - 1 ).getText();
805
806 // #112814# Adapt the start index with the paragraph offset
807 aResult.SegmentStart = mpImpl->Internal2Index( EPosition( aPos.nPara - 1, 0 ) );
808 }
809
810 aResult.SegmentEnd = aResult.SegmentStart + aResult.SegmentText.getLength();
811 }
812 else
813 {
814 // No special handling required, forward to wrapped class
815 aResult = mpImpl->GetParagraph( aPos.nPara ).getTextBeforeIndex( aPos.nIndex, aTextType );
816
817 // #112814# Adapt the start index with the paragraph offset
818 mpImpl->CorrectTextSegment( aResult, aPos.nPara );
819 if ( bLineBreak && (nOldIdx-1) >= 0)
820 {
821 aResult = getTextAtIndex( nOldIdx-1, aTextType );
822 }
823 }
824
825 return aResult;
826 }
827
828 css::accessibility::TextSegment SAL_CALL AccessibleStaticTextBase::getTextBehindIndex( sal_Int32 nIndex, sal_Int16 aTextType )
829 {
830 SolarMutexGuard aGuard;
831
832 sal_Int32 nTemp = nIndex+1;
833 bool bLineBreak = mpImpl->RemoveLineBreakCount( nTemp );
834 mpImpl->RemoveLineBreakCount( nIndex );
835 EPosition aPos( mpImpl->Range2Internal(nIndex) );
836
837 css::accessibility::TextSegment aResult;
838
839 if( AccessibleTextType::PARAGRAPH == aTextType )
840 {
841 // Special casing one behind the last paragraph is not
842 // necessary, this case is invalid here for
843 // getTextBehindIndex
844 if( aPos.nPara + 1 < mpImpl->GetParagraphCount() )
845 {
846 aResult.SegmentText = mpImpl->GetParagraph( aPos.nPara + 1 ).getText();
847
848 // #112814# Adapt the start index with the paragraph offset
849 aResult.SegmentStart = mpImpl->Internal2Index( EPosition( aPos.nPara + 1, 0 ) );
850 aResult.SegmentEnd = aResult.SegmentStart + aResult.SegmentText.getLength();
851 }
852 }
853 else
854 {
855 // No special handling required, forward to wrapped class
856 aResult = mpImpl->GetParagraph( aPos.nPara ).getTextBehindIndex( aPos.nIndex, aTextType );
857
858 // #112814# Adapt the start index with the paragraph offset
859 mpImpl->CorrectTextSegment( aResult, aPos.nPara );
860 if ( bLineBreak )
861 {
862 aResult.SegmentText = OUStringChar(cNewLine) + aResult.SegmentText;
863 }
864 }
865
866 return aResult;
867 }
868
869 sal_Bool SAL_CALL AccessibleStaticTextBase::copyText( sal_Int32 nStartIndex, sal_Int32 nEndIndex )
870 {
871 SolarMutexGuard aGuard;
872
873 if( nStartIndex > nEndIndex )
874 std::swap(nStartIndex, nEndIndex);
875
876 EPosition aStartIndex( mpImpl->Range2Internal(nStartIndex) );
877 EPosition aEndIndex( mpImpl->Range2Internal(nEndIndex) );
878
879 return mpImpl->CopyText( aStartIndex.nPara, aStartIndex.nIndex,
880 aEndIndex.nPara, aEndIndex.nIndex );
881 }
882
883 sal_Bool SAL_CALL AccessibleStaticTextBase::scrollSubstringTo( sal_Int32, sal_Int32, AccessibleScrollType )
884 {
885 return false;
886 }
887
888 // XAccessibleTextAttributes
889 uno::Sequence< beans::PropertyValue > AccessibleStaticTextBase::getDefaultAttributes( const uno::Sequence< OUString >& RequestedAttributes )
890 {
891 // get the intersection of the default attributes of all paragraphs
892
893 SolarMutexGuard aGuard;
894
895 PropertyValueVector aDefAttrVec(
896 comphelper::sequenceToContainer<PropertyValueVector>(mpImpl->GetParagraph( 0 ).getDefaultAttributes( RequestedAttributes )) );
897
898 const sal_Int32 nParaCount = mpImpl->GetParagraphCount();
899 for ( sal_Int32 nPara = 1; nPara < nParaCount; ++nPara )
900 {
901 uno::Sequence< beans::PropertyValue > aSeq = mpImpl->GetParagraph( nPara ).getDefaultAttributes( RequestedAttributes );
902 PropertyValueVector aIntersectionVec;
903
904 for ( const auto& rDefAttr : aDefAttrVec )
905 {
906 const beans::PropertyValue* pItr = aSeq.getConstArray();
907 const beans::PropertyValue* pEnd = pItr + aSeq.getLength();
908 const beans::PropertyValue* pFind = std::find_if( pItr, pEnd, PropertyValueEqualFunctor(rDefAttr) );
909 if ( pFind != pEnd )
910 {
911 aIntersectionVec.push_back( *pFind );
912 }
913 }
914
915 aDefAttrVec.swap( aIntersectionVec );
916
917 if ( aDefAttrVec.empty() )
918 {
919 break;
920 }
921 }
922
923 return comphelper::containerToSequence(aDefAttrVec);
924 }
925
926 uno::Sequence< beans::PropertyValue > SAL_CALL AccessibleStaticTextBase::getRunAttributes( sal_Int32 nIndex, const uno::Sequence< OUString >& RequestedAttributes )
927 {
928 // get those default attributes of the paragraph, which are not part
929 // of the intersection of all paragraphs and add them to the run attributes
930
931 SolarMutexGuard aGuard;
932
933 EPosition aPos( mpImpl->Index2Internal( nIndex ) );
934 AccessibleEditableTextPara& rPara = mpImpl->GetParagraph( aPos.nPara );
935 uno::Sequence< beans::PropertyValue > aDefAttrSeq = rPara.getDefaultAttributes( RequestedAttributes );
936 uno::Sequence< beans::PropertyValue > aRunAttrSeq = rPara.getRunAttributes( aPos.nIndex, RequestedAttributes );
937 uno::Sequence< beans::PropertyValue > aIntersectionSeq = getDefaultAttributes( RequestedAttributes );
938 PropertyValueVector aDiffVec;
939
940 const beans::PropertyValue* pDefAttr = aDefAttrSeq.getConstArray();
941 const sal_Int32 nLength = aDefAttrSeq.getLength();
942 for ( sal_Int32 i = 0; i < nLength; ++i )
943 {
944 const beans::PropertyValue* pItr = aIntersectionSeq.getConstArray();
945 const beans::PropertyValue* pEnd = pItr + aIntersectionSeq.getLength();
946 bool bNone = std::none_of( pItr, pEnd, PropertyValueEqualFunctor( pDefAttr[i] ) );
947 if ( bNone && pDefAttr[i].Handle != 0)
948 {
949 aDiffVec.push_back( pDefAttr[i] );
950 }
951 }
952
953 return ::comphelper::concatSequences( aRunAttrSeq, comphelper::containerToSequence(aDiffVec) );
954 }
955
957 {
958 return mpImpl->GetParagraphBoundingBox();
959 }
960
961} // end of namespace accessibility
962
963
964/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
const beans::PropertyValue & m_rPValue
virtual bool GetAttributeRun(sal_Int32 &nStartIndex, sal_Int32 &nEndIndex, sal_Int32 nPara, sal_Int32 nIndex, bool bInCell=false) const override
Query range of similar attributes.
Definition: unoedprx.cxx:909
Encapsulates EditView and OutlinerView for the purpose of unified EditEngine/Outliner access.
Definition: unoedsrc.hxx:493
virtual bool Copy()=0
Copy current selection to clipboard.
virtual bool SetSelection(const ESelection &rSelection)=0
Set selection in view.
virtual bool GetSelection(ESelection &rSelection) const =0
Query current selection.
void SetEditSource(std::unique_ptr< SvxEditSource > &&pEditSource)
EPosition Index2Internal(sal_Int32 nFlatIndex) const
rtl::Reference< AccessibleEditableTextPara > mxTextParagraph
void SetEventSource(const uno::Reference< XAccessible > &rInterface)
bool CopyText(sal_Int32 nStartPara, sal_Int32 nStartIndex, sal_Int32 nEndPara, sal_Int32 nEndIndex)
EPosition ImpCalcInternal(sal_Int32 nFlatIndex, bool bExclusive) const
bool SetSelection(sal_Int32 nStartPara, sal_Int32 nStartIndex, sal_Int32 nEndPara, sal_Int32 nEndIndex)
AccessibleEditableTextPara & GetParagraph(sal_Int32 nPara) const
void CorrectTextSegment(TextSegment &aTextSegment, int nPara) const
EPosition Range2Internal(sal_Int32 nFlatIndex) const
sal_Int32 Internal2Index(EPosition nEEIndex) const
Helper class for objects containing EditEngine/Outliner text.
virtual sal_Unicode SAL_CALL getCharacter(sal_Int32 nIndex) override
virtual sal_Int32 SAL_CALL getIndexAtPoint(const css::awt::Point &aPoint) override
AccessibleStaticTextBase(::std::unique_ptr< SvxEditSource > &&pEditSource)
Create accessible text object for given edit source.
void SetOffset(const Point &rPoint)
Set offset of EditEngine from parent.
virtual OUString SAL_CALL getSelectedText() override
virtual css::awt::Rectangle SAL_CALL getCharacterBounds(sal_Int32 nIndex) override
virtual sal_Bool SAL_CALL scrollSubstringTo(sal_Int32 nStartIndex, sal_Int32 nEndIndex, css::accessibility::AccessibleScrollType aScrollType) override
virtual css::accessibility::TextSegment SAL_CALL getTextAtIndex(sal_Int32 nIndex, sal_Int16 aTextType) override
Does not support AccessibleTextType::SENTENCE (missing feature in EditEngine)
virtual OUString SAL_CALL getTextRange(sal_Int32 nStartIndex, sal_Int32 nEndIndex) override
virtual sal_Bool SAL_CALL setSelection(sal_Int32 nStartIndex, sal_Int32 nEndIndex) override
This will only work with a functional SvxEditViewForwarder, i.e. an EditEngine/Outliner in edit mode.
virtual css::accessibility::TextSegment SAL_CALL getTextBeforeIndex(sal_Int32 nIndex, sal_Int16 aTextType) override
Does not support AccessibleTextType::SENTENCE (missing feature in EditEngine)
const std::unique_ptr< AccessibleStaticTextBase_Impl > mpImpl
@dyn
virtual sal_Int64 SAL_CALL getAccessibleChildCount()
virtual sal_Int32 SAL_CALL getSelectionStart() override
void Dispose()
Drop all references and enter disposed state.
virtual sal_Int32 SAL_CALL getCharacterCount() override
virtual css::uno::Sequence< css::beans::PropertyValue > SAL_CALL getRunAttributes(sal_Int32 Index, const css::uno::Sequence< OUString > &RequestedAttributes) override
virtual sal_Int32 SAL_CALL getSelectionEnd() override
virtual sal_Int32 SAL_CALL getCaretPosition() override
virtual css::uno::Reference< css::accessibility::XAccessible > SAL_CALL getAccessibleAtPoint(const css::awt::Point &aPoint)
virtual css::uno::Sequence< css::beans::PropertyValue > SAL_CALL getDefaultAttributes(const css::uno::Sequence< OUString > &RequestedAttributes) override
virtual sal_Bool SAL_CALL setCaretPosition(sal_Int32 nIndex) override
void SetEditSource(::std::unique_ptr< SvxEditSource > &&pEditSource)
Set the current edit source.
virtual css::accessibility::TextSegment SAL_CALL getTextBehindIndex(sal_Int32 nIndex, sal_Int16 aTextType) override
Does not support AccessibleTextType::SENTENCE (missing feature in EditEngine)
virtual OUString SAL_CALL getText() override
void SetEventSource(const css::uno::Reference< css::accessibility::XAccessible > &rInterface)
Set the event source.
virtual css::uno::Sequence< css::beans::PropertyValue > SAL_CALL getCharacterAttributes(sal_Int32 nIndex, const css::uno::Sequence< OUString > &aRequestedAttributes) override
virtual css::uno::Reference< css::accessibility::XAccessible > SAL_CALL getAccessibleChild(sal_Int64 i)
virtual sal_Bool SAL_CALL copyText(sal_Int32 nStartIndex, sal_Int32 nEndIndex) override
This will only work with a functional SvxEditViewForwarder, i.e. an EditEngine/Outliner in edit mode.
int nCount
#define DBG_ASSERT(sCon, aError)
#define DBG_TESTSOLARMUTEX()
sal_Int32 nIndex
sal_uInt16 nPos
Sequence< sal_Int8 > aSeq
static ESelection MakeSelection(sal_Int32 nStartPara, sal_Int32 nStartIndex, sal_Int32 nEndPara, sal_Int32 nEndIndex)
sal_Unicode const cNewLine(0x0a)
std::vector< beans::PropertyValue > PropertyValueVector
css::uno::Sequence< DstElementType > containerToSequence(const SrcType &i_Container)
int i
sal_Int32 nIndex
Definition: editdata.hxx:91
sal_Int32 nPara
Definition: editdata.hxx:90
#define SAL_MAX_INT32
unsigned char sal_Bool
sal_uInt16 sal_Unicode
sal_Int32 nLength