xref: /trunk/main/accessibility/source/extended/textwindowaccessibility.cxx (revision 91144cd0085a7583d2099b982122deb2184ab956)
1 /**************************************************************
2  *
3  * Licensed to the Apache Software Foundation (ASF) under one
4  * or more contributor license agreements.  See the NOTICE file
5  * distributed with this work for additional information
6  * regarding copyright ownership.  The ASF licenses this file
7  * to you under the Apache License, Version 2.0 (the
8  * "License"); you may not use this file except in compliance
9  * with the License.  You may obtain a copy of the License at
10  *
11  *   http://www.apache.org/licenses/LICENSE-2.0
12  *
13  * Unless required by applicable law or agreed to in writing,
14  * software distributed under the License is distributed on an
15  * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
16  * KIND, either express or implied.  See the License for the
17  * specific language governing permissions and limitations
18  * under the License.
19  *
20  *************************************************************/
21 
22 
23 
24 // MARKER(update_precomp.py): autogen include statement, do not remove
25 #include "precompiled_accessibility.hxx"
26 
27 #ifndef _TOOLKIT_AWT_VCLXACCESSIBLECOMPONENT_HXX_
28 #include <accessibility/extended/textwindowaccessibility.hxx>
29 #endif
30 #include "comphelper/accessibleeventnotifier.hxx"
31 #include "unotools/accessiblerelationsethelper.hxx"
32 #include <unotools/accessiblestatesethelper.hxx>
33 #include <vcl/window.hxx>
34 #include <toolkit/helper/convert.hxx>
35 
36 #include <algorithm>
37 #include <vector>
38 #include <hash_map>
39 
40 namespace css = ::com::sun::star;
41 
42 namespace accessibility
43 {
44     ::sal_Int32 getSelectionType(::sal_Int32 nNewFirstPara, ::sal_Int32 nNewFirstPos, ::sal_Int32 nNewLastPara, ::sal_Int32 nNewLastPos);
45     void sendEvent(::sal_Int32 start, ::sal_Int32 end, ::sal_Int16 nEventId);
46 
47 // Both ::osl::Mutex and ParagraphBase implement acquire and release, and thus
48 // ::rtl::Reference< Paragraph > does not work.  So ParagraphImpl was factored
49 // out and ::rtl::Reference< ParagraphImpl > is used instead.
50 class Paragraph: private ::osl::Mutex, public ParagraphImpl
51 {
52 public:
Paragraph(::rtl::Reference<Document> const & rDocument,Paragraphs::size_type nNumber)53     inline Paragraph(::rtl::Reference< Document > const & rDocument,
54                      Paragraphs::size_type nNumber):
55         ParagraphImpl(rDocument, nNumber, *this) {}
56 };
57 
startListening(::SfxBroadcaster & rNotifier)58 void SfxListenerGuard::startListening(::SfxBroadcaster & rNotifier)
59 {
60     OSL_ENSURE(m_pNotifier == 0, "called more than once");
61     m_pNotifier = &rNotifier;
62     m_rListener.StartListening(*m_pNotifier, true);
63 }
64 
endListening()65 void SfxListenerGuard::endListening()
66 {
67     if (m_pNotifier != 0)
68     {
69         m_rListener.EndListening(*m_pNotifier);
70         m_pNotifier = 0;
71     }
72 }
73 
startListening(::Window & rNotifier)74 void WindowListenerGuard::startListening(::Window & rNotifier)
75 {
76     OSL_ENSURE(m_pNotifier == 0, "called more than once");
77     m_pNotifier = &rNotifier;
78     m_pNotifier->AddEventListener(m_aListener);
79 }
80 
endListening()81 void WindowListenerGuard::endListening()
82 {
83     if (m_pNotifier != 0)
84     {
85         m_pNotifier->RemoveEventListener(m_aListener);
86         m_pNotifier = 0;
87     }
88 }
89 
ParagraphImpl(::rtl::Reference<Document> const & rDocument,Paragraphs::size_type nNumber,::osl::Mutex & rMutex)90 ParagraphImpl::ParagraphImpl(::rtl::Reference< Document > const & rDocument,
91                              Paragraphs::size_type nNumber,
92                              ::osl::Mutex & rMutex):
93     ParagraphBase(rMutex),
94     m_xDocument(rDocument),
95     m_nNumber(nNumber),
96     m_nClientId(0)
97 {
98     m_aParagraphText = m_xDocument->retrieveParagraphText(this);
99 }
100 
101 void
numberChanged(bool bIncremented)102 ParagraphImpl::numberChanged(bool bIncremented)
103 {
104     if (bIncremented)
105         ++m_nNumber;
106     else
107         --m_nNumber;
108 }
109 
textChanged()110 void ParagraphImpl::textChanged()
111 {
112     ::rtl::OUString aParagraphText = implGetText();
113     ::css::uno::Any aOldValue, aNewValue;
114     if ( implInitTextChangedEvent( m_aParagraphText, aParagraphText, aOldValue, aNewValue ) )
115     {
116         m_aParagraphText = aParagraphText;
117         notifyEvent(::css::accessibility::AccessibleEventId::
118                     TEXT_CHANGED,
119                     aOldValue, aNewValue);
120     }
121 }
122 
notifyEvent(::sal_Int16 nEventId,::css::uno::Any const & rOldValue,::css::uno::Any const & rNewValue)123 void ParagraphImpl::notifyEvent(::sal_Int16 nEventId,
124                                 ::css::uno::Any const & rOldValue,
125                                 ::css::uno::Any const & rNewValue)
126 {
127     if (m_nClientId)
128         comphelper::AccessibleEventNotifier::addEvent( m_nClientId, ::css::accessibility::AccessibleEventObject(
129                              static_cast< ::cppu::OWeakObject * >(this),
130                              nEventId, rNewValue, rOldValue) );
131 }
132 
133 // virtual
134 ::css::uno::Reference< ::css::accessibility::XAccessibleContext > SAL_CALL
getAccessibleContext()135 ParagraphImpl::getAccessibleContext()
136 {
137     checkDisposed();
138     return this;
139 }
140 
141 // virtual
getAccessibleChildCount()142 ::sal_Int32 SAL_CALL ParagraphImpl::getAccessibleChildCount()
143 {
144     checkDisposed();
145     return 0;
146 }
147 
148 // virtual
149 ::css::uno::Reference< ::css::accessibility::XAccessible > SAL_CALL
getAccessibleChild(::sal_Int32)150 ParagraphImpl::getAccessibleChild(::sal_Int32)
151 {
152     checkDisposed();
153     throw ::css::lang::IndexOutOfBoundsException(
154         ::rtl::OUString(
155             RTL_CONSTASCII_USTRINGPARAM(
156                 "textwindowaccessibility.cxx:"
157                 " ParagraphImpl::getAccessibleChild")),
158         static_cast< ::css::uno::XWeak * >(this));
159 }
160 
161 // virtual
162 ::css::uno::Reference< ::css::accessibility::XAccessible > SAL_CALL
getAccessibleParent()163 ParagraphImpl::getAccessibleParent()
164 {
165     checkDisposed();
166     return m_xDocument->getAccessible();
167 }
168 
169 // virtual
getAccessibleIndexInParent()170 ::sal_Int32 SAL_CALL ParagraphImpl::getAccessibleIndexInParent()
171 {
172     checkDisposed();
173     return m_xDocument->retrieveParagraphIndex(this);
174 }
175 
176 // virtual
getAccessibleRole()177 ::sal_Int16 SAL_CALL ParagraphImpl::getAccessibleRole()
178 {
179     checkDisposed();
180     return ::css::accessibility::AccessibleRole::PARAGRAPH;
181 }
182 
183 // virtual
getAccessibleDescription()184 ::rtl::OUString SAL_CALL ParagraphImpl::getAccessibleDescription()
185 {
186     checkDisposed();
187     return ::rtl::OUString();
188 }
189 
190 // virtual
getAccessibleName()191 ::rtl::OUString SAL_CALL ParagraphImpl::getAccessibleName()
192 {
193     checkDisposed();
194     return ::rtl::OUString();
195 }
196 
197 // virtual
198 ::css::uno::Reference< ::css::accessibility::XAccessibleRelationSet >
getAccessibleRelationSet()199 SAL_CALL ParagraphImpl::getAccessibleRelationSet()
200 {
201     checkDisposed();
202     return m_xDocument->retrieveParagraphRelationSet( this );
203 }
204 
205 // virtual
206 ::css::uno::Reference< ::css::accessibility::XAccessibleStateSet >
getAccessibleStateSet()207 SAL_CALL ParagraphImpl::getAccessibleStateSet()
208 {
209     checkDisposed();
210 
211     // FIXME  Notification of changes (STATE_CHANGED) missing when
212     // m_rView.IsReadOnly() changes:
213     return new ::utl::AccessibleStateSetHelper(
214         m_xDocument->retrieveParagraphState(this));
215 }
216 
217 // virtual
getLocale()218 ::css::lang::Locale SAL_CALL ParagraphImpl::getLocale()
219 {
220     checkDisposed();
221     return m_xDocument->retrieveLocale();
222 }
223 
224 // virtual
containsPoint(::css::awt::Point const & rPoint)225 ::sal_Bool SAL_CALL ParagraphImpl::containsPoint(::css::awt::Point const & rPoint)
226 {
227     checkDisposed();
228     ::css::awt::Rectangle aRect(m_xDocument->retrieveParagraphBounds(this,
229                                                                      false));
230     return rPoint.X >= 0 && rPoint.X < aRect.Width
231         && rPoint.Y >= 0 && rPoint.Y < aRect.Height;
232 }
233 
234 // virtual
235 ::css::uno::Reference< ::css::accessibility::XAccessible > SAL_CALL
getAccessibleAtPoint(::css::awt::Point const &)236 ParagraphImpl::getAccessibleAtPoint(::css::awt::Point const &)
237 {
238     checkDisposed();
239     return 0;
240 }
241 
242 // virtual
getBounds()243 ::css::awt::Rectangle SAL_CALL ParagraphImpl::getBounds()
244 {
245     checkDisposed();
246     return m_xDocument->retrieveParagraphBounds(this, false);
247 }
248 
249 // virtual
getLocation()250 ::css::awt::Point SAL_CALL ParagraphImpl::getLocation()
251 {
252     checkDisposed();
253     ::css::awt::Rectangle aRect(m_xDocument->retrieveParagraphBounds(this,
254                                                                      false));
255     return ::css::awt::Point(aRect.X, aRect.Y);
256 }
257 
258 // virtual
getLocationOnScreen()259 ::css::awt::Point SAL_CALL ParagraphImpl::getLocationOnScreen()
260 {
261     checkDisposed();
262     ::css::awt::Rectangle aRect(m_xDocument->retrieveParagraphBounds(this,
263                                                                      true));
264     return ::css::awt::Point(aRect.X, aRect.Y);
265 }
266 
267 // virtual
getSize()268 ::css::awt::Size SAL_CALL ParagraphImpl::getSize()
269 {
270     checkDisposed();
271     ::css::awt::Rectangle aRect(m_xDocument->retrieveParagraphBounds(this,
272                                                                      false));
273     return ::css::awt::Size(aRect.Width, aRect.Height);
274 }
275 
276 // virtual
grabFocus()277 void SAL_CALL ParagraphImpl::grabFocus()
278 {
279     checkDisposed();
280     Window* pWindow = m_xDocument->GetWindow();
281     if ( pWindow )
282     {
283         pWindow->GrabFocus();
284     }
285     try
286     {
287         m_xDocument->changeParagraphSelection(this, 0, 0);
288     }
289     catch (::css::lang::IndexOutOfBoundsException & rEx)
290     {
291         OSL_TRACE(
292             "textwindowaccessibility.cxx: ParagraphImpl::grabFocus:"
293             " caught unexpected %s\n",
294             ::rtl::OUStringToOString(rEx.Message, RTL_TEXTENCODING_UTF8).
295             getStr());
296     }
297 }
298 
299 // virtual
getAccessibleKeyBinding()300 ::css::uno::Any SAL_CALL ParagraphImpl::getAccessibleKeyBinding()
301 {
302     checkDisposed();
303     return ::css::uno::Any();
304 }
305 
306 // virtual
getForeground()307 ::css::util::Color SAL_CALL ParagraphImpl::getForeground()
308 {
309     return 0; // TODO
310 }
311 
312 // virtual
getBackground()313 ::css::util::Color SAL_CALL ParagraphImpl::getBackground()
314 {
315     return 0; // TODO
316 }
317 
318 // virtual
getCaretPosition()319 ::sal_Int32 SAL_CALL ParagraphImpl::getCaretPosition()
320 {
321     checkDisposed();
322     return m_xDocument->retrieveParagraphCaretPosition(this);
323 }
324 
325 // virtual
setCaretPosition(::sal_Int32 nIndex)326 ::sal_Bool SAL_CALL ParagraphImpl::setCaretPosition(::sal_Int32 nIndex)
327 {
328     checkDisposed();
329     m_xDocument->changeParagraphSelection(this, nIndex, nIndex);
330     return true;
331 }
332 
333 // virtual
getCharacter(::sal_Int32 nIndex)334 ::sal_Unicode SAL_CALL ParagraphImpl::getCharacter(::sal_Int32 nIndex)
335 {
336     checkDisposed();
337     return OCommonAccessibleText::getCharacter(nIndex);
338 }
339 
340 // virtual
341 ::css::uno::Sequence< ::css::beans::PropertyValue > SAL_CALL
getCharacterAttributes(::sal_Int32 nIndex,const::com::sun::star::uno::Sequence<::rtl::OUString> & aRequestedAttributes)342 ParagraphImpl::getCharacterAttributes(::sal_Int32 nIndex, const ::com::sun::star::uno::Sequence< ::rtl::OUString >& aRequestedAttributes)
343 {
344     checkDisposed();
345     return m_xDocument->retrieveCharacterAttributes( this, nIndex, aRequestedAttributes );
346 }
347 
348 // virtual
349 ::css::awt::Rectangle SAL_CALL
getCharacterBounds(::sal_Int32 nIndex)350 ParagraphImpl::getCharacterBounds(::sal_Int32 nIndex)
351 {
352     checkDisposed();
353     ::css::awt::Rectangle aBounds(m_xDocument->retrieveCharacterBounds(this, nIndex));
354     ::css::awt::Rectangle aParaBounds(m_xDocument->retrieveParagraphBounds(this, false));
355     aBounds.X -= aParaBounds.X;
356     aBounds.Y -= aParaBounds.Y;
357     return aBounds;
358 }
359 
360 // virtual
getCharacterCount()361 ::sal_Int32 SAL_CALL ParagraphImpl::getCharacterCount()
362 {
363     checkDisposed();
364     return OCommonAccessibleText::getCharacterCount();
365 }
366 
367 // virtual
368 ::sal_Int32 SAL_CALL
getIndexAtPoint(::css::awt::Point const & rPoint)369 ParagraphImpl::getIndexAtPoint(::css::awt::Point const & rPoint)
370 {
371     checkDisposed();
372     ::css::awt::Point aPoint(rPoint);
373     ::css::awt::Rectangle aParaBounds(m_xDocument->retrieveParagraphBounds(this, false));
374     aPoint.X += aParaBounds.X;
375     aPoint.Y += aParaBounds.Y;
376     return m_xDocument->retrieveCharacterIndex(this, aPoint);
377 }
378 
379 // virtual
getSelectedText()380 ::rtl::OUString SAL_CALL ParagraphImpl::getSelectedText()
381 {
382     checkDisposed();
383 
384     return OCommonAccessibleText::getSelectedText();
385 }
386 
387 // virtual
getSelectionStart()388 ::sal_Int32 SAL_CALL ParagraphImpl::getSelectionStart()
389 {
390     checkDisposed();
391     return OCommonAccessibleText::getSelectionStart();
392 }
393 
394 // virtual
getSelectionEnd()395 ::sal_Int32 SAL_CALL ParagraphImpl::getSelectionEnd()
396 {
397     checkDisposed();
398     return OCommonAccessibleText::getSelectionEnd();
399 }
400 
401 // virtual
setSelection(::sal_Int32 nStartIndex,::sal_Int32 nEndIndex)402 ::sal_Bool SAL_CALL ParagraphImpl::setSelection(::sal_Int32 nStartIndex,
403                                                 ::sal_Int32 nEndIndex)
404 {
405     checkDisposed();
406     m_xDocument->changeParagraphSelection(this, nStartIndex, nEndIndex);
407     return true;
408 }
409 
410 // virtual
getText()411 ::rtl::OUString SAL_CALL ParagraphImpl::getText()
412 {
413     checkDisposed();
414     return OCommonAccessibleText::getText();
415 }
416 
417 // virtual
getTextRange(::sal_Int32 nStartIndex,::sal_Int32 nEndIndex)418 ::rtl::OUString SAL_CALL ParagraphImpl::getTextRange(::sal_Int32 nStartIndex,
419                                                      ::sal_Int32 nEndIndex)
420 {
421     checkDisposed();
422     return OCommonAccessibleText::getTextRange(nStartIndex, nEndIndex);
423 }
424 
425 // virtual
getTextAtIndex(sal_Int32 nIndex,sal_Int16 aTextType)426 ::com::sun::star::accessibility::TextSegment SAL_CALL ParagraphImpl::getTextAtIndex( sal_Int32 nIndex, sal_Int16 aTextType )
427 {
428     checkDisposed();
429     return OCommonAccessibleText::getTextAtIndex(nIndex, aTextType);
430 }
431 
432 // virtual
getTextBeforeIndex(sal_Int32 nIndex,sal_Int16 aTextType)433 ::com::sun::star::accessibility::TextSegment SAL_CALL ParagraphImpl::getTextBeforeIndex( sal_Int32 nIndex, sal_Int16 aTextType )
434 {
435     checkDisposed();
436     return OCommonAccessibleText::getTextBeforeIndex(nIndex, aTextType);
437 }
438 
439 // virtual
getTextBehindIndex(sal_Int32 nIndex,sal_Int16 aTextType)440 ::com::sun::star::accessibility::TextSegment SAL_CALL ParagraphImpl::getTextBehindIndex( sal_Int32 nIndex, sal_Int16 aTextType )
441 {
442     checkDisposed();
443     return OCommonAccessibleText::getTextBehindIndex(nIndex, aTextType);
444 }
445 
446 // virtual
copyText(::sal_Int32 nStartIndex,::sal_Int32 nEndIndex)447 ::sal_Bool SAL_CALL ParagraphImpl::copyText(::sal_Int32 nStartIndex,
448                                             ::sal_Int32 nEndIndex)
449 {
450     checkDisposed();
451     m_xDocument->copyParagraphText(this, nStartIndex, nEndIndex);
452     return true;
453 }
454 
455 // virtual
cutText(::sal_Int32 nStartIndex,::sal_Int32 nEndIndex)456 ::sal_Bool SAL_CALL ParagraphImpl::cutText(::sal_Int32 nStartIndex,
457                                            ::sal_Int32 nEndIndex)
458 {
459     checkDisposed();
460     m_xDocument->changeParagraphText(this, nStartIndex, nEndIndex, true, false,
461                                      ::rtl::OUString());
462     return true;
463 }
464 
465 // virtual
pasteText(::sal_Int32 nIndex)466 ::sal_Bool SAL_CALL ParagraphImpl::pasteText(::sal_Int32 nIndex)
467 {
468     checkDisposed();
469     m_xDocument->changeParagraphText(this, nIndex, nIndex, false, true,
470                                      ::rtl::OUString());
471     return true;
472 }
473 
474 // virtual
deleteText(::sal_Int32 nStartIndex,::sal_Int32 nEndIndex)475 ::sal_Bool SAL_CALL ParagraphImpl::deleteText(::sal_Int32 nStartIndex,
476                                           ::sal_Int32 nEndIndex)
477 {
478     checkDisposed();
479     m_xDocument->changeParagraphText(this, nStartIndex, nEndIndex, false, false,
480                                      ::rtl::OUString());
481     return true;
482 }
483 
484 // virtual
insertText(::rtl::OUString const & rText,::sal_Int32 nIndex)485 ::sal_Bool SAL_CALL ParagraphImpl::insertText(::rtl::OUString const & rText,
486                                               ::sal_Int32 nIndex)
487 {
488     checkDisposed();
489     m_xDocument->changeParagraphText(this, nIndex, nIndex, false, false, rText);
490     return true;
491 }
492 
493 // virtual
494 ::sal_Bool SAL_CALL
replaceText(::sal_Int32 nStartIndex,::sal_Int32 nEndIndex,::rtl::OUString const & rReplacement)495 ParagraphImpl::replaceText(::sal_Int32 nStartIndex, ::sal_Int32 nEndIndex,
496                            ::rtl::OUString const & rReplacement)
497 {
498     checkDisposed();
499     m_xDocument->changeParagraphText(this, nStartIndex, nEndIndex, false, false,
500                                      rReplacement);
501     return true;
502 }
503 
504 // virtual
setAttributes(::sal_Int32 nStartIndex,::sal_Int32 nEndIndex,::css::uno::Sequence<::css::beans::PropertyValue> const & rAttributeSet)505 ::sal_Bool SAL_CALL ParagraphImpl::setAttributes(
506     ::sal_Int32 nStartIndex, ::sal_Int32 nEndIndex,
507     ::css::uno::Sequence< ::css::beans::PropertyValue > const & rAttributeSet)
508 {
509     checkDisposed();
510     m_xDocument->changeParagraphAttributes(this, nStartIndex, nEndIndex,
511                                            rAttributeSet);
512     return true;
513 }
514 
515 // virtual
setText(::rtl::OUString const & rText)516 ::sal_Bool SAL_CALL ParagraphImpl::setText(::rtl::OUString const & rText)
517 {
518     checkDisposed();
519     m_xDocument->changeParagraphText(this, rText);
520     return true;
521 }
522 
523 // virtual
524 ::css::uno::Sequence< ::css::beans::PropertyValue > SAL_CALL
getDefaultAttributes(const::css::uno::Sequence<::rtl::OUString> & RequestedAttributes)525 ParagraphImpl::getDefaultAttributes(const ::css::uno::Sequence< ::rtl::OUString >& RequestedAttributes)
526 {
527     checkDisposed();
528     return m_xDocument->retrieveDefaultAttributes( this, RequestedAttributes );
529 }
530 
531 // virtual
532 ::css::uno::Sequence< ::css::beans::PropertyValue > SAL_CALL
getRunAttributes(::sal_Int32 Index,const::css::uno::Sequence<::rtl::OUString> & RequestedAttributes)533 ParagraphImpl::getRunAttributes(::sal_Int32 Index, const ::css::uno::Sequence< ::rtl::OUString >& RequestedAttributes)
534 {
535     checkDisposed();
536     return m_xDocument->retrieveRunAttributes( this, Index, RequestedAttributes );
537 }
538 
539 // virtual
getLineNumberAtIndex(::sal_Int32 nIndex)540 ::sal_Int32 SAL_CALL ParagraphImpl::getLineNumberAtIndex( ::sal_Int32 nIndex )
541 {
542     checkDisposed();
543 
544     ::sal_Int32 nLineNo = -1;
545     ::css::i18n::Boundary aBoundary =
546         m_xDocument->retrieveParagraphLineBoundary( this, nIndex, &nLineNo );
547 
548     return nLineNo;
549 }
550 
551 // virtual
getTextAtLineNumber(::sal_Int32 nLineNo)552 ::css::accessibility::TextSegment SAL_CALL ParagraphImpl::getTextAtLineNumber( ::sal_Int32 nLineNo )
553 {
554     checkDisposed();
555 
556     ::css::i18n::Boundary aBoundary =
557         m_xDocument->retrieveParagraphBoundaryOfLine( this, nLineNo );
558 
559     return ::css::accessibility::TextSegment( getTextRange(aBoundary.startPos, aBoundary.endPos),
560         aBoundary.startPos, aBoundary.endPos);
561 }
562 
563 // virtual
getTextAtLineWithCaret()564 ::css::accessibility::TextSegment SAL_CALL ParagraphImpl::getTextAtLineWithCaret(  )
565 {
566     checkDisposed();
567 
568     sal_Int32 nLineNo = getNumberOfLineWithCaret();
569 
570     try {
571         return ( nLineNo >= 0 ) ?
572             getTextAtLineNumber( nLineNo ) :
573             ::css::accessibility::TextSegment();
574     } catch (const ::css::lang::IndexOutOfBoundsException&) {
575         throw ::css::uno::RuntimeException(
576             ::rtl::OUString(
577                 RTL_CONSTASCII_USTRINGPARAM(
578                     "textwindowaccessibility.cxx:"
579                     " ParagraphImpl::getTextAtLineWithCaret") ),
580             static_cast< ::css::uno::XWeak * >( this ) );
581     }
582 }
583 
584 // virtual
getNumberOfLineWithCaret()585 ::sal_Int32 SAL_CALL ParagraphImpl::getNumberOfLineWithCaret(  )
586 {
587     checkDisposed();
588     return m_xDocument->retrieveParagraphLineWithCursor(this);
589 }
590 
591 
592 // virtual
addEventListener(::css::uno::Reference<::css::accessibility::XAccessibleEventListener> const & rListener)593 void SAL_CALL ParagraphImpl::addEventListener(
594     ::css::uno::Reference<
595     ::css::accessibility::XAccessibleEventListener > const & rListener)
596 {
597     if (rListener.is())
598     {
599         ::osl::ClearableMutexGuard aGuard(rBHelper.rMutex);
600         if (rBHelper.bDisposed || rBHelper.bInDispose)
601         {
602             aGuard.clear();
603             rListener->disposing(::css::lang::EventObject(
604                                     static_cast< ::cppu::OWeakObject * >(this)));
605         }
606         else
607         {
608             if (!m_nClientId)
609                 m_nClientId = comphelper::AccessibleEventNotifier::registerClient( );
610             comphelper::AccessibleEventNotifier::addEventListener( m_nClientId, rListener );
611         }
612     }
613 }
614 
615 // virtual
removeEventListener(::css::uno::Reference<::css::accessibility::XAccessibleEventListener> const & rListener)616 void SAL_CALL ParagraphImpl::removeEventListener(
617     ::css::uno::Reference<
618     ::css::accessibility::XAccessibleEventListener > const & rListener)
619 {
620     comphelper::AccessibleEventNotifier::TClientId nId = 0;
621     {
622         ::osl::ClearableMutexGuard aGuard(rBHelper.rMutex);
623         if (rListener.is() && m_nClientId != 0
624             && comphelper::AccessibleEventNotifier::removeEventListener( m_nClientId, rListener ) == 0)
625         {
626             nId = m_nClientId;
627             m_nClientId = 0;
628         }
629     }
630     if (nId != 0)
631     {
632         // no listeners anymore
633         // -> revoke ourself. This may lead to the notifier thread dying (if we were the last client),
634         // and at least to us not firing any events anymore, in case somebody calls
635         // NotifyAccessibleEvent, again
636         comphelper::AccessibleEventNotifier::revokeClient(nId);
637     }
638 }
639 
640 // virtual
disposing()641 void SAL_CALL ParagraphImpl::disposing()
642 {
643     comphelper::AccessibleEventNotifier::TClientId nId = 0;
644     {
645         ::osl::ClearableMutexGuard aGuard(rBHelper.rMutex);
646         nId = m_nClientId;
647         m_nClientId = 0;
648     }
649     if (nId != 0)
650         comphelper::AccessibleEventNotifier::revokeClientNotifyDisposing(nId, *this);
651 }
652 
653 // virtual
implGetText()654 ::rtl::OUString ParagraphImpl::implGetText()
655 {
656     return m_xDocument->retrieveParagraphText(this);
657 }
658 
659 // virtual
implGetLocale()660 ::css::lang::Locale ParagraphImpl::implGetLocale()
661 {
662     return m_xDocument->retrieveLocale();
663 }
664 
665 // virtual
implGetSelection(::sal_Int32 & rStartIndex,::sal_Int32 & rEndIndex)666 void ParagraphImpl::implGetSelection(::sal_Int32 & rStartIndex,
667                                      ::sal_Int32 & rEndIndex)
668 {
669     m_xDocument->retrieveParagraphSelection(this, &rStartIndex, &rEndIndex);
670 }
671 
672 // virtual
implGetParagraphBoundary(::css::i18n::Boundary & rBoundary,::sal_Int32 nIndex)673 void ParagraphImpl::implGetParagraphBoundary( ::css::i18n::Boundary& rBoundary,
674                                               ::sal_Int32 nIndex )
675 {
676     ::rtl::OUString sText( implGetText() );
677     ::sal_Int32 nLength = sText.getLength();
678 
679     if ( implIsValidIndex( nIndex, nLength ) )
680     {
681         rBoundary.startPos = 0;
682         rBoundary.endPos = nLength;
683     }
684     else
685     {
686         rBoundary.startPos = nIndex;
687         rBoundary.endPos = nIndex;
688     }
689 }
690 
691 // virtual
implGetLineBoundary(::css::i18n::Boundary & rBoundary,::sal_Int32 nIndex)692 void ParagraphImpl::implGetLineBoundary( ::css::i18n::Boundary& rBoundary,
693                                          ::sal_Int32 nIndex )
694 {
695     ::rtl::OUString sText( implGetText() );
696     ::sal_Int32 nLength = sText.getLength();
697 
698     if ( implIsValidIndex( nIndex, nLength ) || nIndex == nLength )
699     {
700         ::css::i18n::Boundary aBoundary =
701             m_xDocument->retrieveParagraphLineBoundary( this, nIndex );
702         rBoundary.startPos = aBoundary.startPos;
703         rBoundary.endPos = aBoundary.endPos;
704     }
705     else
706     {
707         rBoundary.startPos = nIndex;
708         rBoundary.endPos = nIndex;
709     }
710 }
711 
712 
checkDisposed()713 void ParagraphImpl::checkDisposed()
714 {
715     ::osl::MutexGuard aGuard(rBHelper.rMutex);
716     if (!(rBHelper.bDisposed || rBHelper.bInDispose))
717         return;
718     throw ::css::lang::DisposedException(
719         ::rtl::OUString(), static_cast< ::css::uno::XWeak * >(this));
720 }
721 
Document(::VCLXWindow * pVclXWindow,::TextEngine & rEngine,::TextView & rView,bool bCompoundControlChild)722 Document::Document(::VCLXWindow * pVclXWindow, ::TextEngine & rEngine,
723                    ::TextView & rView, bool bCompoundControlChild):
724     VCLXAccessibleComponent(pVclXWindow),
725     m_xAccessible(pVclXWindow),
726     m_rEngine(rEngine),
727     m_rView(rView),
728     m_aEngineListener(*this),
729     m_aViewListener(LINK(this, Document, WindowEventHandler)),
730     m_bCompoundControlChild(bCompoundControlChild)
731 {}
732 
retrieveLocale()733 ::css::lang::Locale Document::retrieveLocale()
734 {
735     ::osl::Guard< ::comphelper::IMutex > aExternalGuard(getExternalLock());
736     return m_rEngine.GetLocale();
737 }
738 
retrieveParagraphIndex(ParagraphImpl const * pParagraph)739 ::sal_Int32 Document::retrieveParagraphIndex(ParagraphImpl const * pParagraph)
740 {
741     ::osl::MutexGuard aInternalGuard(GetMutex());
742 
743     // If a client holds on to a Paragraph that is no longer visible, it can
744     // happen that this Paragraph lies outside the range from m_aVisibleBegin
745     // to m_aVisibleEnd.  In that case, return -1 instead of a valid index:
746     Paragraphs::iterator aPara(m_xParagraphs->begin()
747                                + pParagraph->getNumber());
748     return aPara < m_aVisibleBegin || aPara >= m_aVisibleEnd
749         ? -1 : static_cast< ::sal_Int32 >(aPara - m_aVisibleBegin);
750         // XXX  numeric overflow
751 }
752 
retrieveParagraphState(ParagraphImpl const * pParagraph)753 ::sal_Int64 Document::retrieveParagraphState(ParagraphImpl const * pParagraph)
754 {
755     ::osl::MutexGuard aInternalGuard(GetMutex());
756 
757     // If a client holds on to a Paragraph that is no longer visible, it can
758     // happen that this Paragraph lies outside the range from m_aVisibleBegin
759     // to m_aVisibleEnd.  In that case, it is neither VISIBLE nor SHOWING:
760     ::sal_Int64 nState
761           = (static_cast< ::sal_Int64 >(1)
762              << ::css::accessibility::AccessibleStateType::ENABLED)
763           | (static_cast< ::sal_Int64 >(1)
764              << ::css::accessibility::AccessibleStateType::SENSITIVE)
765           | (static_cast< ::sal_Int64 >(1)
766              << ::css::accessibility::AccessibleStateType::FOCUSABLE)
767           | (static_cast< ::sal_Int64 >(1)
768              << ::css::accessibility::AccessibleStateType::MULTI_LINE);
769     if (!m_rView.IsReadOnly())
770         nState |= (static_cast< ::sal_Int64 >(1)
771                    << ::css::accessibility::AccessibleStateType::EDITABLE);
772     Paragraphs::iterator aPara(m_xParagraphs->begin()
773                                + pParagraph->getNumber());
774     if (aPara >= m_aVisibleBegin && aPara < m_aVisibleEnd)
775     {
776         nState
777             |= (static_cast< ::sal_Int64 >(1)
778                 << ::css::accessibility::AccessibleStateType::VISIBLE)
779             | (static_cast< ::sal_Int64 >(1)
780                << ::css::accessibility::AccessibleStateType::SHOWING);
781         if (aPara == m_aFocused)
782             nState |= (static_cast< ::sal_Int64 >(1)
783                        << ::css::accessibility::AccessibleStateType::FOCUSED);
784     }
785     return nState;
786 };
787 
788 ::css::awt::Rectangle
retrieveParagraphBounds(ParagraphImpl const * pParagraph,bool bAbsolute)789 Document::retrieveParagraphBounds(ParagraphImpl const * pParagraph,
790                                   bool bAbsolute)
791 {
792     ::osl::Guard< ::comphelper::IMutex > aExternalGuard(getExternalLock());
793     ::osl::MutexGuard aInternalGuard(GetMutex());
794 
795     // If a client holds on to a Paragraph that is no longer visible (as it
796     // scrolled out the top of the view), it can happen that this Paragraph
797     // lies before m_aVisibleBegin.  In that case, calculate the vertical
798     // position of the Paragraph starting at paragraph 0, otherwise optimize
799     // and start at m_aVisibleBegin:
800     Paragraphs::iterator aPara(m_xParagraphs->begin()
801                                + pParagraph->getNumber());
802     ::sal_Int32 nPos;
803     Paragraphs::iterator aIt;
804     if (aPara < m_aVisibleBegin)
805     {
806         nPos = 0;
807         aIt = m_xParagraphs->begin();
808     }
809     else
810     {
811         nPos = m_nViewOffset - m_nVisibleBeginOffset;
812         aIt = m_aVisibleBegin;
813     }
814     for (; aIt != aPara; ++aIt)
815         nPos += aIt->getHeight();
816 
817     Point aOrig(0, 0);
818     if (bAbsolute)
819         aOrig = m_rView.GetWindow()->OutputToAbsoluteScreenPixel(aOrig);
820 
821     return ::css::awt::Rectangle(
822         static_cast< ::sal_Int32 >(aOrig.X()),
823         static_cast< ::sal_Int32 >(aOrig.Y()) + nPos - m_nViewOffset,
824         m_rView.GetWindow()->GetOutputSizePixel().Width(), aPara->getHeight());
825         // XXX  numeric overflow (3x)
826 }
827 
828 ::rtl::OUString
retrieveParagraphText(ParagraphImpl const * pParagraph)829 Document::retrieveParagraphText(ParagraphImpl const * pParagraph)
830 {
831     ::osl::Guard< ::comphelper::IMutex > aExternalGuard(getExternalLock());
832     ::osl::MutexGuard aInternalGuard(GetMutex());
833     return m_rEngine.GetText(static_cast< ::sal_uLong >(pParagraph->getNumber()));
834         // numeric overflow cannot happen here
835 }
836 
retrieveParagraphSelection(ParagraphImpl const * pParagraph,::sal_Int32 * pBegin,::sal_Int32 * pEnd)837 void Document::retrieveParagraphSelection(ParagraphImpl const * pParagraph,
838                                           ::sal_Int32 * pBegin,
839                                           ::sal_Int32 * pEnd)
840 {
841     ::osl::Guard< ::comphelper::IMutex > aExternalGuard(getExternalLock());
842     ::osl::MutexGuard aInternalGuard(GetMutex());
843     ::TextSelection const & rSelection = m_rView.GetSelection();
844     Paragraphs::size_type nNumber = pParagraph->getNumber();
845     TextPaM aStartPaM( rSelection.GetStart() );
846     TextPaM aEndPaM( rSelection.GetEnd() );
847     TextPaM aMinPaM( ::std::min( aStartPaM, aEndPaM ) );
848     TextPaM aMaxPaM( ::std::max( aStartPaM, aEndPaM ) );
849 
850     if ( nNumber >= aMinPaM.GetPara() && nNumber <= aMaxPaM.GetPara() )
851     {
852         *pBegin = nNumber > aMinPaM.GetPara()
853             ? 0
854             : static_cast< ::sal_Int32 >( aMinPaM.GetIndex() );
855             // XXX numeric overflow
856         *pEnd = nNumber < aMaxPaM.GetPara()
857             ? static_cast< ::sal_Int32 >( m_rEngine.GetText(static_cast< ::sal_uLong >(nNumber)).Len() )
858             : static_cast< ::sal_Int32 >( aMaxPaM.GetIndex() );
859             // XXX  numeric overflow (3x)
860 
861         if ( aStartPaM > aEndPaM )
862             ::std::swap( *pBegin, *pEnd );
863     }
864     else
865     {
866         *pBegin = 0;
867         *pEnd = 0;
868     }
869 }
870 
retrieveParagraphCaretPosition(ParagraphImpl const * pParagraph)871 ::sal_Int32 Document::retrieveParagraphCaretPosition(ParagraphImpl const * pParagraph)
872 {
873     ::osl::Guard< ::comphelper::IMutex > aExternalGuard(getExternalLock());
874     ::osl::MutexGuard aInternalGuard(GetMutex());
875     ::TextSelection const & rSelection = m_rView.GetSelection();
876     Paragraphs::size_type nNumber = pParagraph->getNumber();
877     TextPaM aEndPaM( rSelection.GetEnd() );
878 
879     return aEndPaM.GetPara() == nNumber
880         ? static_cast< ::sal_Int32 >(aEndPaM.GetIndex()) : -1;
881 }
882 
883 ::css::awt::Rectangle
retrieveCharacterBounds(ParagraphImpl const * pParagraph,::sal_Int32 nIndex)884 Document::retrieveCharacterBounds(ParagraphImpl const * pParagraph,
885                                   ::sal_Int32 nIndex)
886 {
887     ::osl::Guard< ::comphelper::IMutex > aExternalGuard(getExternalLock());
888     ::osl::MutexGuard aInternalGuard(GetMutex());
889     ::sal_uLong nNumber = static_cast< ::sal_uLong >(pParagraph->getNumber());
890     sal_Int32 nLength = m_rEngine.GetText(nNumber).Len();
891         // XXX  numeric overflow
892     if (nIndex < 0 || nIndex > nLength)
893         throw ::css::lang::IndexOutOfBoundsException(
894             ::rtl::OUString(
895                 RTL_CONSTASCII_USTRINGPARAM(
896                     "textwindowaccessibility.cxx:"
897                     " Document::retrieveCharacterAttributes")),
898             static_cast< ::css::uno::XWeak * >(this));
899     ::css::awt::Rectangle aBounds( 0, 0, 0, 0 );
900     if ( nIndex == nLength )
901     {
902         aBounds = AWTRectangle(
903             m_rEngine.PaMtoEditCursor(::TextPaM(nNumber,
904                                                 static_cast< ::sal_uInt16 >(nIndex))));
905     }
906     else
907     {
908         ::Rectangle aLeft(
909             m_rEngine.PaMtoEditCursor(::TextPaM(nNumber,
910                                                 static_cast< ::sal_uInt16 >(nIndex))));
911             // XXX  numeric overflow
912         ::Rectangle aRight(
913             m_rEngine.PaMtoEditCursor(::TextPaM(nNumber,
914                                                 static_cast< ::sal_uInt16 >(nIndex)
915                                                 + 1)));
916             // XXX  numeric overflow (2x)
917         // FIXME  If the vertical extends of the two cursors do not match, assume
918         // nIndex is the last character on the line; the bounding box will then
919         // extend to m_rEnginge.GetMaxTextWidth():
920         ::sal_Int32 nWidth = (aLeft.Top() == aRight.Top()
921                             && aLeft.Bottom() == aRight.Bottom())
922             ? static_cast< ::sal_Int32 >(aRight.Left() - aLeft.Left())
923             : static_cast< ::sal_Int32 >(m_rEngine.GetMaxTextWidth()
924                                         - aLeft.Left());
925             // XXX  numeric overflow (4x)
926         aBounds = ::css::awt::Rectangle(static_cast< ::sal_Int32 >(aLeft.Left()),
927                                         static_cast< ::sal_Int32 >(aLeft.Top() - m_nViewOffset),
928                                         nWidth,
929                                         static_cast< ::sal_Int32 >(aLeft.Bottom()
930                                                                     - aLeft.Top()));
931             // XXX  numeric overflow (4x)
932     }
933     return aBounds;
934 }
935 
retrieveCharacterIndex(ParagraphImpl const * pParagraph,::css::awt::Point const & rPoint)936 ::sal_Int32 Document::retrieveCharacterIndex(ParagraphImpl const * pParagraph,
937                                              ::css::awt::Point const & rPoint)
938 {
939     ::osl::Guard< ::comphelper::IMutex > aExternalGuard(getExternalLock());
940     ::osl::MutexGuard aInternalGuard(GetMutex());
941     ::sal_uLong nNumber = static_cast< ::sal_uLong >(pParagraph->getNumber());
942         // XXX  numeric overflow
943     ::TextPaM aPaM(m_rEngine.GetPaM(::Point(static_cast< long >(rPoint.X),
944                                             static_cast< long >(rPoint.Y))));
945         // XXX  numeric overflow (2x)
946     return aPaM.GetPara() == nNumber
947         ? static_cast< ::sal_Int32 >(aPaM.GetIndex()) : -1;
948         // XXX  numeric overflow
949 }
950 
951 struct IndexCompare
952 {
953     const ::css::beans::PropertyValue* pValues;
IndexCompareaccessibility::IndexCompare954     IndexCompare( const ::css::beans::PropertyValue* pVals ) : pValues(pVals) {}
operator ()accessibility::IndexCompare955     bool operator() ( const sal_Int32& a, const sal_Int32& b ) const
956     {
957         return (pValues[a].Name < pValues[b].Name) ? true : false;
958     }
959 };
960 
961 ::css::uno::Sequence< ::css::beans::PropertyValue >
retrieveCharacterAttributes(ParagraphImpl const * pParagraph,::sal_Int32 nIndex,const::css::uno::Sequence<::rtl::OUString> & aRequestedAttributes)962 Document::retrieveCharacterAttributes(
963     ParagraphImpl const * pParagraph, ::sal_Int32 nIndex,
964     const ::css::uno::Sequence< ::rtl::OUString >& aRequestedAttributes)
965 {
966     ::osl::Guard< ::comphelper::IMutex > aExternalGuard(getExternalLock());
967 
968     Font aFont = m_rEngine.GetFont();
969     const sal_Int32 AttributeCount = 9;
970     sal_Int32 i = 0;
971     ::css::uno::Sequence< ::css::beans::PropertyValue > aAttribs( AttributeCount );
972     //character background color
973     {
974         aAttribs[i].Name = ::rtl::OUString(RTL_CONSTASCII_USTRINGPARAM("CharBackColor"));
975         aAttribs[i].Handle = -1;
976         aAttribs[i].Value = mapFontColor( aFont.GetFillColor() );
977         aAttribs[i].State = ::css::beans::PropertyState_DIRECT_VALUE;
978         i++;
979     }
980     //character color
981     {
982         aAttribs[i].Name = ::rtl::OUString(RTL_CONSTASCII_USTRINGPARAM("CharColor"));
983         aAttribs[i].Handle = -1;
984         //aAttribs[i].Value = mapFontColor( aFont.GetColor() );
985         aAttribs[i].Value = mapFontColor( m_rEngine.GetTextColor() );
986         aAttribs[i].State = ::css::beans::PropertyState_DIRECT_VALUE;
987         i++;
988     }
989     //character font name
990     {
991         aAttribs[i].Name = ::rtl::OUString(RTL_CONSTASCII_USTRINGPARAM("CharFontName"));
992         aAttribs[i].Handle = -1;
993         aAttribs[i].Value = ::css::uno::makeAny( (::rtl::OUString)aFont.GetName() );
994         aAttribs[i].State = ::css::beans::PropertyState_DIRECT_VALUE;
995         i++;
996     }
997     //character height
998     {
999         aAttribs[i].Name = ::rtl::OUString(RTL_CONSTASCII_USTRINGPARAM("CharHeight"));
1000         aAttribs[i].Handle = -1;
1001         aAttribs[i].Value = ::css::uno::makeAny( (sal_Int16)aFont.GetHeight() );
1002         aAttribs[i].State = ::css::beans::PropertyState_DIRECT_VALUE;
1003         i++;
1004     }
1005     //character posture
1006     {
1007         aAttribs[i].Name = ::rtl::OUString(RTL_CONSTASCII_USTRINGPARAM("CharPosture"));
1008         aAttribs[i].Handle = -1;
1009         aAttribs[i].Value = ::css::uno::makeAny( (sal_Int16)aFont.GetItalic() );
1010         aAttribs[i].State = ::css::beans::PropertyState_DIRECT_VALUE;
1011         i++;
1012     }
1013     //character relief
1014     /*{
1015         aAttribs[i].Name = ::rtl::OUString(RTL_CONSTASCII_USTRINGPARAM("CharRelief"));
1016         aAttribs[i].Handle = -1;
1017         aAttribs[i].Value = ::css::uno::makeAny( (sal_Int16)aFont.GetRelief() );
1018         aAttribs[i].State = ::css::beans::PropertyState_DIRECT_VALUE;
1019         i++;
1020     }*/
1021     //character strikeout
1022     {
1023         aAttribs[i].Name = ::rtl::OUString(RTL_CONSTASCII_USTRINGPARAM("CharStrikeout"));
1024         aAttribs[i].Handle = -1;
1025         aAttribs[i].Value = ::css::uno::makeAny( (sal_Int16)aFont.GetStrikeout() );
1026         aAttribs[i].State = ::css::beans::PropertyState_DIRECT_VALUE;
1027         i++;
1028     }
1029     //character underline
1030     {
1031         aAttribs[i].Name = ::rtl::OUString(RTL_CONSTASCII_USTRINGPARAM("CharUnderline"));
1032         aAttribs[i].Handle = -1;
1033         aAttribs[i].Value = ::css::uno::makeAny( (sal_Int16)aFont.GetUnderline() );
1034         aAttribs[i].State = ::css::beans::PropertyState_DIRECT_VALUE;
1035         i++;
1036     }
1037     //character weight
1038     {
1039         aAttribs[i].Name = ::rtl::OUString(RTL_CONSTASCII_USTRINGPARAM("CharWeight"));
1040         aAttribs[i].Handle = -1;
1041         aAttribs[i].Value = ::css::uno::makeAny( (float)aFont.GetWeight() );
1042         aAttribs[i].State = ::css::beans::PropertyState_DIRECT_VALUE;
1043         i++;
1044     }
1045     //character alignment
1046     {
1047         aAttribs[i].Name = ::rtl::OUString(RTL_CONSTASCII_USTRINGPARAM("ParaAdjust"));
1048         aAttribs[i].Handle = -1;
1049         aAttribs[i].Value = ::css::uno::makeAny( (sal_Int16)m_rEngine.GetTextAlign() );
1050         aAttribs[i].State = ::css::beans::PropertyState_DIRECT_VALUE;
1051         i++;
1052     }
1053     ::osl::MutexGuard aInternalGuard(GetMutex());
1054     ::sal_uLong nNumber = static_cast< ::sal_uLong >(pParagraph->getNumber());
1055         // XXX  numeric overflow
1056     // nIndex can be equal to Len();
1057     //if (nIndex < 0 || nIndex >= m_rEngine.GetText(nNumber).Len())
1058     if (nIndex < 0 || nIndex > m_rEngine.GetText(nNumber).Len())
1059         throw ::css::lang::IndexOutOfBoundsException(
1060             ::rtl::OUString(
1061                 RTL_CONSTASCII_USTRINGPARAM(
1062                     "textwindowaccessibility.cxx:"
1063                     " Document::retrieveCharacterAttributes")),
1064             static_cast< ::css::uno::XWeak * >(this));
1065 
1066     // retrieve default attributes
1067     tPropValMap aCharAttrSeq;
1068     retrieveDefaultAttributesImpl( pParagraph, aRequestedAttributes, aCharAttrSeq );
1069 
1070     // retrieve run attributes
1071     tPropValMap aRunAttrSeq;
1072     retrieveRunAttributesImpl( pParagraph, nIndex, aRequestedAttributes, aRunAttrSeq );
1073 
1074     // merge default and run attributes
1075     for ( tPropValMap::const_iterator aRunIter  = aRunAttrSeq.begin();
1076           aRunIter != aRunAttrSeq.end();
1077           ++aRunIter )
1078     {
1079         aCharAttrSeq[ aRunIter->first ] = aRunIter->second;
1080     }
1081 
1082     ::css::beans::PropertyValue* pValues = aAttribs.getArray();
1083     for (i = 0; i < AttributeCount; i++,pValues++)
1084     {
1085         aCharAttrSeq[ pValues->Name ] = *pValues;
1086     }
1087 
1088     ::css::uno::Sequence< ::css::beans::PropertyValue > aRes = convertHashMapToSequence( aCharAttrSeq );
1089 
1090     // sort the attributes
1091     sal_Int32 nLength = aRes.getLength();
1092     const ::css::beans::PropertyValue* pPairs = aRes.getConstArray();
1093     sal_Int32* pIndices = new sal_Int32[nLength];
1094     for( i = 0; i < nLength; i++ )
1095         pIndices[i] = i;
1096     std::sort( &pIndices[0], &pIndices[nLength], IndexCompare(pPairs) );
1097     // create sorted sequences according to index array
1098     ::css::uno::Sequence< ::css::beans::PropertyValue > aNewValues( nLength );
1099     ::css::beans::PropertyValue* pNewValues = aNewValues.getArray();
1100     for( i = 0; i < nLength; i++ )
1101     {
1102         pNewValues[i] = pPairs[pIndices[i]];
1103     }
1104     delete[] pIndices;
1105 
1106     return aNewValues;
1107 }
1108 
retrieveDefaultAttributesImpl(ParagraphImpl const * pParagraph,const::css::uno::Sequence<::rtl::OUString> & RequestedAttributes,tPropValMap & rDefAttrSeq)1109 void Document::retrieveDefaultAttributesImpl(
1110     ParagraphImpl const * pParagraph,
1111     const ::css::uno::Sequence< ::rtl::OUString >& RequestedAttributes,
1112     tPropValMap& rDefAttrSeq)
1113 {
1114     // default attributes are not supported by text engine
1115     (void) pParagraph;
1116     (void) RequestedAttributes;
1117     (void) rDefAttrSeq;
1118 }
1119 
1120 ::css::uno::Sequence< ::css::beans::PropertyValue >
retrieveDefaultAttributes(ParagraphImpl const * pParagraph,const::css::uno::Sequence<::rtl::OUString> & RequestedAttributes)1121 Document::retrieveDefaultAttributes(
1122     ParagraphImpl const * pParagraph,
1123     const ::css::uno::Sequence< ::rtl::OUString >& RequestedAttributes)
1124 {
1125     ::osl::Guard< ::comphelper::IMutex > aExternalGuard( getExternalLock() );
1126     ::osl::MutexGuard aInternalGuard( GetMutex() );
1127 
1128     tPropValMap aDefAttrSeq;
1129     retrieveDefaultAttributesImpl( pParagraph, RequestedAttributes, aDefAttrSeq );
1130     return convertHashMapToSequence( aDefAttrSeq );
1131 }
1132 
1133 // static
1134 ::css::uno::Sequence< ::css::beans::PropertyValue >
convertHashMapToSequence(tPropValMap & rAttrSeq)1135 Document::convertHashMapToSequence(tPropValMap& rAttrSeq)
1136 {
1137     ::css::uno::Sequence< ::css::beans::PropertyValue > aValues( rAttrSeq.size() );
1138     ::css::beans::PropertyValue* pValues = aValues.getArray();
1139     ::sal_Int32 i = 0;
1140     for ( tPropValMap::const_iterator aIter  = rAttrSeq.begin();
1141           aIter != rAttrSeq.end();
1142           ++aIter )
1143     {
1144         pValues[i] = aIter->second;
1145         ++i;
1146     }
1147     return aValues;
1148 }
1149 
retrieveRunAttributesImpl(ParagraphImpl const * pParagraph,::sal_Int32 Index,const::css::uno::Sequence<::rtl::OUString> & RequestedAttributes,tPropValMap & rRunAttrSeq)1150 void Document::retrieveRunAttributesImpl(
1151     ParagraphImpl const * pParagraph, ::sal_Int32 Index,
1152     const ::css::uno::Sequence< ::rtl::OUString >& RequestedAttributes,
1153     tPropValMap& rRunAttrSeq)
1154 {
1155     ::sal_uLong nNumber = static_cast< ::sal_uLong >( pParagraph->getNumber() );
1156     ::TextPaM aPaM( nNumber, static_cast< ::sal_uInt16 >( Index ) );
1157         // XXX  numeric overflow
1158     // FIXME  TEXTATTR_HYPERLINK ignored:
1159     ::TextAttribFontColor const * pColor
1160           = static_cast< ::TextAttribFontColor const * >(
1161               m_rEngine.FindAttrib( aPaM, TEXTATTR_FONTCOLOR ) );
1162     ::TextAttribFontWeight const * pWeight
1163           = static_cast< ::TextAttribFontWeight const * >(
1164               m_rEngine.FindAttrib( aPaM, TEXTATTR_FONTWEIGHT ) );
1165     tPropValMap aRunAttrSeq;
1166     if ( pColor )
1167     {
1168         ::css::beans::PropertyValue aPropVal;
1169         aPropVal.Name =
1170             ::rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( "CharColor" ) );
1171         aPropVal.Handle = -1;
1172         aPropVal.Value = mapFontColor( pColor->GetColor() );
1173         aPropVal.State = ::css::beans::PropertyState_DIRECT_VALUE;
1174         aRunAttrSeq[ aPropVal.Name ] = aPropVal;
1175     }
1176     if ( pWeight )
1177     {
1178         ::css::beans::PropertyValue aPropVal;
1179         aPropVal.Name =
1180             ::rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( "CharWeight" ) );
1181         aPropVal.Handle = -1;
1182         aPropVal.Value = mapFontWeight( pWeight->getFontWeight() );
1183         aPropVal.State = ::css::beans::PropertyState_DIRECT_VALUE;
1184         aRunAttrSeq[ aPropVal.Name ] = aPropVal;
1185     }
1186     if ( RequestedAttributes.getLength() == 0 )
1187     {
1188         rRunAttrSeq = aRunAttrSeq;
1189     }
1190     else
1191     {
1192         const ::rtl::OUString* pReqAttrs = RequestedAttributes.getConstArray();
1193         const ::sal_Int32 nLength = RequestedAttributes.getLength();
1194         for ( ::sal_Int32 i = 0; i < nLength; ++i )
1195         {
1196             tPropValMap::iterator aIter = aRunAttrSeq.find( pReqAttrs[i] );
1197             if ( aIter != aRunAttrSeq.end() )
1198             {
1199                 rRunAttrSeq[ (*aIter).first ] = (*aIter).second;
1200             }
1201         }
1202     }
1203 }
1204 
1205 ::css::uno::Sequence< ::css::beans::PropertyValue >
retrieveRunAttributes(ParagraphImpl const * pParagraph,::sal_Int32 Index,const::css::uno::Sequence<::rtl::OUString> & RequestedAttributes)1206 Document::retrieveRunAttributes(
1207     ParagraphImpl const * pParagraph, ::sal_Int32 Index,
1208     const ::css::uno::Sequence< ::rtl::OUString >& RequestedAttributes)
1209 {
1210     ::osl::Guard< ::comphelper::IMutex > aExternalGuard( getExternalLock() );
1211     ::osl::MutexGuard aInternalGuard( GetMutex() );
1212     ::sal_uLong nNumber = static_cast< ::sal_uLong >( pParagraph->getNumber() );
1213         // XXX  numeric overflow
1214     if ( Index < 0 || Index >= m_rEngine.GetText(nNumber).Len() )
1215         throw ::css::lang::IndexOutOfBoundsException(
1216             ::rtl::OUString(
1217                 RTL_CONSTASCII_USTRINGPARAM(
1218                     "textwindowaccessibility.cxx:"
1219                     " Document::retrieveRunAttributes") ),
1220             static_cast< ::css::uno::XWeak * >( this ) );
1221 
1222     tPropValMap aRunAttrSeq;
1223     retrieveRunAttributesImpl( pParagraph, Index, RequestedAttributes, aRunAttrSeq );
1224     return convertHashMapToSequence( aRunAttrSeq );
1225 }
1226 
changeParagraphText(ParagraphImpl * pParagraph,::rtl::OUString const & rText)1227 void Document::changeParagraphText(ParagraphImpl * pParagraph,
1228                                    ::rtl::OUString const & rText)
1229 {
1230     ::osl::Guard< ::comphelper::IMutex > aExternalGuard(getExternalLock());
1231     {
1232         ::osl::MutexGuard aInternalGuard(GetMutex());
1233         ::sal_uLong nNumber = static_cast< ::sal_uLong >(pParagraph->getNumber());
1234             // XXX  numeric overflow
1235         changeParagraphText(nNumber, 0, m_rEngine.GetTextLen(nNumber), false,
1236                             false, rText);
1237     }
1238 }
1239 
changeParagraphText(ParagraphImpl * pParagraph,::sal_Int32 nBegin,::sal_Int32 nEnd,bool bCut,bool bPaste,::rtl::OUString const & rText)1240 void Document::changeParagraphText(ParagraphImpl * pParagraph,
1241                                    ::sal_Int32 nBegin, ::sal_Int32 nEnd,
1242                                    bool bCut, bool bPaste,
1243                                    ::rtl::OUString const & rText)
1244 {
1245     ::osl::Guard< ::comphelper::IMutex > aExternalGuard(getExternalLock());
1246     {
1247         ::osl::MutexGuard aInternalGuard(GetMutex());
1248         ::sal_uLong nNumber = static_cast< ::sal_uLong >(pParagraph->getNumber());
1249             // XXX  numeric overflow
1250         if (nBegin < 0 || nBegin > nEnd
1251             || nEnd > m_rEngine.GetText(nNumber).Len())
1252             throw ::css::lang::IndexOutOfBoundsException(
1253                 ::rtl::OUString(
1254                     RTL_CONSTASCII_USTRINGPARAM(
1255                         "textwindowaccessibility.cxx:"
1256                         " Document::changeParagraphText")),
1257                 static_cast< ::css::uno::XWeak * >(this));
1258         changeParagraphText(nNumber, static_cast< ::sal_uInt16 >(nBegin),
1259                             static_cast< ::sal_uInt16 >(nEnd), bCut, bPaste, rText);
1260             // XXX  numeric overflow (2x)
1261     }
1262 }
1263 
copyParagraphText(ParagraphImpl const * pParagraph,::sal_Int32 nBegin,::sal_Int32 nEnd)1264 void Document::copyParagraphText(ParagraphImpl const * pParagraph,
1265                                  ::sal_Int32 nBegin, ::sal_Int32 nEnd)
1266 {
1267     ::osl::Guard< ::comphelper::IMutex > aExternalGuard(getExternalLock());
1268     {
1269         ::osl::MutexGuard aInternalGuard(GetMutex());
1270         ::sal_uLong nNumber = static_cast< ::sal_uLong >(pParagraph->getNumber());
1271             // XXX  numeric overflow
1272         if (nBegin < 0 || nBegin > nEnd
1273             || nEnd > m_rEngine.GetText(nNumber).Len())
1274             throw ::css::lang::IndexOutOfBoundsException(
1275                 ::rtl::OUString(RTL_CONSTASCII_USTRINGPARAM(
1276                                     "textwindowaccessibility.cxx:"
1277                                     " Document::copyParagraphText")),
1278                 static_cast< ::css::uno::XWeak * >(this));
1279         m_rView.SetSelection(
1280             ::TextSelection(::TextPaM(nNumber, static_cast< ::sal_uInt16 >(nBegin)),
1281                             ::TextPaM(nNumber, static_cast< ::sal_uInt16 >(nEnd))));
1282             // XXX  numeric overflow (2x)
1283         m_rView.Copy();
1284     }
1285 }
1286 
changeParagraphAttributes(ParagraphImpl * pParagraph,::sal_Int32 nBegin,::sal_Int32 nEnd,::css::uno::Sequence<::css::beans::PropertyValue> const & rAttributeSet)1287 void Document::changeParagraphAttributes(
1288     ParagraphImpl * pParagraph, ::sal_Int32 nBegin, ::sal_Int32 nEnd,
1289     ::css::uno::Sequence< ::css::beans::PropertyValue > const & rAttributeSet)
1290 {
1291     ::osl::Guard< ::comphelper::IMutex > aExternalGuard(getExternalLock());
1292     {
1293         ::osl::MutexGuard aInternalGuard(GetMutex());
1294         ::sal_uLong nNumber = static_cast< ::sal_uLong >(pParagraph->getNumber());
1295         // XXX  numeric overflow
1296         if (nBegin < 0 || nBegin > nEnd
1297             || nEnd > m_rEngine.GetText(nNumber).Len())
1298             throw ::css::lang::IndexOutOfBoundsException(
1299                 ::rtl::OUString(
1300                     RTL_CONSTASCII_USTRINGPARAM(
1301                         "textwindowaccessibility.cxx:"
1302                         " Document::changeParagraphAttributes")),
1303                 static_cast< ::css::uno::XWeak * >(this));
1304 
1305         // FIXME  The new attributes are added to any attributes already set,
1306         // they do not replace the old attributes as required by
1307         // XAccessibleEditableText.setAttributes:
1308         for (::sal_Int32 i = 0; i < rAttributeSet.getLength(); ++i)
1309             if (rAttributeSet[i].Name.equalsAsciiL(
1310                     RTL_CONSTASCII_STRINGPARAM("CharColor")))
1311                 m_rEngine.SetAttrib(::TextAttribFontColor(
1312                                         mapFontColor(rAttributeSet[i].Value)),
1313                                     nNumber, static_cast< ::sal_uInt16 >(nBegin),
1314                                     static_cast< ::sal_uInt16 >(nEnd));
1315                     // XXX  numeric overflow (2x)
1316             else if (rAttributeSet[i].Name.equalsAsciiL(
1317                          RTL_CONSTASCII_STRINGPARAM("CharWeight")))
1318                 m_rEngine.SetAttrib(::TextAttribFontWeight(
1319                                         mapFontWeight(rAttributeSet[i].Value)),
1320                                     nNumber, static_cast< ::sal_uInt16 >(nBegin),
1321                                     static_cast< ::sal_uInt16 >(nEnd));
1322                     // XXX  numeric overflow (2x)
1323     }
1324 }
1325 
changeParagraphSelection(ParagraphImpl * pParagraph,::sal_Int32 nBegin,::sal_Int32 nEnd)1326 void Document::changeParagraphSelection(ParagraphImpl * pParagraph,
1327                                         ::sal_Int32 nBegin, ::sal_Int32 nEnd)
1328 {
1329     ::osl::Guard< ::comphelper::IMutex > aExternalGuard(getExternalLock());
1330     {
1331         ::osl::MutexGuard aInternalGuard(GetMutex());
1332         ::sal_uLong nNumber = static_cast< ::sal_uLong >(pParagraph->getNumber());
1333             // XXX  numeric overflow
1334         if (nBegin < 0 || nBegin > nEnd
1335             || nEnd > m_rEngine.GetText(nNumber).Len())
1336             throw ::css::lang::IndexOutOfBoundsException(
1337                 ::rtl::OUString(RTL_CONSTASCII_USTRINGPARAM(
1338                                     "textwindowaccessibility.cxx:"
1339                                     " Document::changeParagraphSelection")),
1340                 static_cast< ::css::uno::XWeak * >(this));
1341         m_rView.SetSelection(
1342             ::TextSelection(::TextPaM(nNumber, static_cast< ::sal_uInt16 >(nBegin)),
1343                             ::TextPaM(nNumber, static_cast< ::sal_uInt16 >(nEnd))));
1344             // XXX  numeric overflow (2x)
1345     }
1346 }
1347 
1348 ::css::i18n::Boundary
retrieveParagraphLineBoundary(ParagraphImpl const * pParagraph,::sal_Int32 nIndex,::sal_Int32 * pLineNo)1349 Document::retrieveParagraphLineBoundary( ParagraphImpl const * pParagraph,
1350                                          ::sal_Int32 nIndex, ::sal_Int32 *pLineNo )
1351 {
1352     ::css::i18n::Boundary aBoundary;
1353     aBoundary.startPos = nIndex;
1354     aBoundary.endPos = nIndex;
1355 
1356     ::osl::Guard< ::comphelper::IMutex > aExternalGuard( getExternalLock() );
1357     {
1358         ::osl::MutexGuard aInternalGuard( GetMutex() );
1359         ::sal_uLong nNumber = static_cast< ::sal_uLong >( pParagraph->getNumber() );
1360         if ( nIndex < 0 || nIndex > m_rEngine.GetText( nNumber ).Len() )
1361             throw ::css::lang::IndexOutOfBoundsException(
1362                 ::rtl::OUString(
1363                     RTL_CONSTASCII_USTRINGPARAM(
1364                         "textwindowaccessibility.cxx:"
1365                         " Document::retrieveParagraphLineBoundary" ) ),
1366                 static_cast< ::css::uno::XWeak * >( this ) );
1367         ::sal_Int32 nLineStart = 0;
1368         ::sal_Int32 nLineEnd = 0;
1369         ::sal_uInt16 nLineCount = m_rEngine.GetLineCount( nNumber );
1370         for ( ::sal_uInt16 nLine = 0; nLine < nLineCount; ++nLine )
1371         {
1372             ::sal_Int32 nLineLength = static_cast< ::sal_Int32 >(
1373                 m_rEngine.GetLineLen( nNumber, nLine ) );
1374             nLineStart = nLineEnd;
1375             nLineEnd += nLineLength;
1376             if ( nIndex >= nLineStart && ( ( nLine == nLineCount - 1 ) ? nIndex <= nLineEnd : nIndex < nLineEnd ) )
1377             {
1378                 aBoundary.startPos = nLineStart;
1379                 aBoundary.endPos = nLineEnd;
1380                 if( pLineNo )
1381                     pLineNo[0] = nLine;
1382                 break;
1383             }
1384         }
1385     }
1386 
1387     return aBoundary;
1388 }
1389 
1390 ::css::i18n::Boundary
retrieveParagraphBoundaryOfLine(ParagraphImpl const * pParagraph,::sal_Int32 nLineNo)1391 Document::retrieveParagraphBoundaryOfLine( ParagraphImpl const * pParagraph,
1392                                            ::sal_Int32 nLineNo )
1393 {
1394     ::css::i18n::Boundary aBoundary;
1395     aBoundary.startPos = 0;
1396     aBoundary.endPos = 0;
1397 
1398     ::osl::Guard< ::comphelper::IMutex > aExternalGuard( getExternalLock() );
1399     {
1400         ::osl::MutexGuard aInternalGuard( GetMutex() );
1401         ::sal_uLong nNumber = static_cast< ::sal_uLong >( pParagraph->getNumber() );
1402         if ( nLineNo >= m_rEngine.GetLineCount( nNumber ) )
1403             throw ::css::lang::IndexOutOfBoundsException(
1404                 ::rtl::OUString(
1405                     RTL_CONSTASCII_USTRINGPARAM(
1406                         "textwindowaccessibility.cxx:"
1407                         " Document::retrieveParagraphBoundaryOfLine" ) ),
1408                 static_cast< ::css::uno::XWeak * >( this ) );
1409         ::sal_Int32 nLineStart = 0;
1410         ::sal_Int32 nLineEnd = 0;
1411         for ( ::sal_uInt16 nLine = 0; nLine <= nLineNo; ++nLine )
1412         {
1413             ::sal_Int32 nLineLength = static_cast< ::sal_Int32 >(
1414                 m_rEngine.GetLineLen( nNumber, nLine ) );
1415             nLineStart = nLineEnd;
1416             nLineEnd += nLineLength;
1417         }
1418 
1419         aBoundary.startPos = nLineStart;
1420         aBoundary.endPos = nLineEnd;
1421     }
1422 
1423     return aBoundary;
1424 }
1425 
retrieveParagraphLineWithCursor(ParagraphImpl const * pParagraph)1426 sal_Int32 Document::retrieveParagraphLineWithCursor( ParagraphImpl const * pParagraph )
1427 {
1428     ::osl::Guard< ::comphelper::IMutex > aExternalGuard(getExternalLock());
1429     ::osl::MutexGuard aInternalGuard(GetMutex());
1430     ::TextSelection const & rSelection = m_rView.GetSelection();
1431     Paragraphs::size_type nNumber = pParagraph->getNumber();
1432     TextPaM aEndPaM( rSelection.GetEnd() );
1433 
1434     return aEndPaM.GetPara() == nNumber
1435         ? m_rView.GetLineNumberOfCursorInSelection() : -1;
1436 }
1437 
1438 
1439 ::css::uno::Reference< ::css::accessibility::XAccessibleRelationSet >
retrieveParagraphRelationSet(ParagraphImpl const * pParagraph)1440 Document::retrieveParagraphRelationSet( ParagraphImpl const * pParagraph )
1441 {
1442     ::osl::MutexGuard aInternalGuard( GetMutex() );
1443 
1444     ::utl::AccessibleRelationSetHelper* pRelationSetHelper = new ::utl::AccessibleRelationSetHelper();
1445     ::css::uno::Reference< ::css::accessibility::XAccessibleRelationSet > xSet = pRelationSetHelper;
1446 
1447     Paragraphs::iterator aPara( m_xParagraphs->begin() + pParagraph->getNumber() );
1448 
1449     if ( aPara > m_aVisibleBegin && aPara < m_aVisibleEnd )
1450     {
1451         ::css::uno::Sequence< ::css::uno::Reference< ::css::uno::XInterface > > aSequence(1);
1452         aSequence[0] = getAccessibleChild( aPara - 1 );
1453         ::css::accessibility::AccessibleRelation aRelation( ::css::accessibility::AccessibleRelationType::CONTENT_FLOWS_FROM, aSequence );
1454         pRelationSetHelper->AddRelation( aRelation );
1455     }
1456 
1457     if ( aPara >= m_aVisibleBegin && aPara < m_aVisibleEnd -1 )
1458     {
1459         ::css::uno::Sequence< ::css::uno::Reference< ::css::uno::XInterface > > aSequence(1);
1460         aSequence[0] = getAccessibleChild( aPara + 1 );
1461         ::css::accessibility::AccessibleRelation aRelation( ::css::accessibility::AccessibleRelationType::CONTENT_FLOWS_TO, aSequence );
1462         pRelationSetHelper->AddRelation( aRelation );
1463     }
1464 
1465     return xSet;
1466 }
1467 
ProcessWindowEvent(const VclWindowEvent & rVclWindowEvent)1468 void Document::ProcessWindowEvent( const VclWindowEvent& rVclWindowEvent )
1469 {
1470     VCLXAccessibleComponent::ProcessWindowEvent( rVclWindowEvent );
1471 }
1472 
1473 // virtual
getAccessibleChildCount()1474 ::sal_Int32 SAL_CALL Document::getAccessibleChildCount()
1475 {
1476     ::comphelper::OExternalLockGuard aGuard(this);
1477     init();
1478     return m_aVisibleEnd - m_aVisibleBegin;
1479 }
1480 
1481 // virtual
1482 ::css::uno::Reference< ::css::accessibility::XAccessible > SAL_CALL
getAccessibleChild(::sal_Int32 i)1483 Document::getAccessibleChild(::sal_Int32 i)
1484 {
1485     ::comphelper::OExternalLockGuard aGuard(this);
1486     init();
1487     if (i < 0 || i >= m_aVisibleEnd - m_aVisibleBegin)
1488         throw ::css::lang::IndexOutOfBoundsException(
1489             ::rtl::OUString(
1490                 RTL_CONSTASCII_USTRINGPARAM(
1491                     "textwindowaccessibility.cxx:"
1492                     " Document::getAccessibleChild")),
1493             static_cast< ::css::uno::XWeak * >(this));
1494     return getAccessibleChild(m_aVisibleBegin
1495                               + static_cast< Paragraphs::size_type >(i));
1496 }
1497 
1498 // virtual
getAccessibleRole()1499 ::sal_Int16 SAL_CALL Document::getAccessibleRole()
1500 {
1501     return ::css::accessibility::AccessibleRole::TEXT_FRAME;
1502 }
1503 
1504 // virtual
1505 ::css::uno::Reference< ::css::accessibility::XAccessible > SAL_CALL
getAccessibleAtPoint(::css::awt::Point const & rPoint)1506 Document::getAccessibleAtPoint(::css::awt::Point const & rPoint)
1507 {
1508     ::comphelper::OExternalLockGuard aGuard(this);
1509     init();
1510     if (rPoint.X >= 0
1511         && rPoint.X < m_rView.GetWindow()->GetOutputSizePixel().Width()
1512         && rPoint.Y >= 0 && rPoint.Y < m_nViewHeight)
1513     {
1514         ::sal_Int32 nOffset = m_nViewOffset + rPoint.Y; // XXX  numeric overflow
1515         ::sal_Int32 nPos = m_nViewOffset - m_nVisibleBeginOffset;
1516         for (Paragraphs::iterator aIt(m_aVisibleBegin); aIt != m_aVisibleEnd;
1517              ++aIt)
1518         {
1519             nPos += aIt->getHeight(); // XXX  numeric overflow
1520             if (nOffset < nPos)
1521                 return getAccessibleChild(aIt);
1522         }
1523     }
1524     return 0;
1525 }
FillAccessibleStateSet(utl::AccessibleStateSetHelper & rStateSet)1526 void Document::FillAccessibleStateSet( utl::AccessibleStateSetHelper& rStateSet )
1527 {
1528     VCLXAccessibleComponent::FillAccessibleStateSet( rStateSet );
1529     if (!m_rView.IsReadOnly())
1530         rStateSet.AddState( ::css::accessibility::AccessibleStateType::EDITABLE );
1531 }
1532 
FillAccessibleRelationSet(utl::AccessibleRelationSetHelper & rRelationSet)1533 void    Document::FillAccessibleRelationSet( utl::AccessibleRelationSetHelper& rRelationSet )
1534 {
1535     if( getAccessibleParent()->getAccessibleContext()->getAccessibleRole() == ::css::accessibility::AccessibleRole::SCROLL_PANE )
1536     {
1537         ::css::uno::Sequence< ::css::uno::Reference< ::css::uno::XInterface > > aSequence(1);
1538         aSequence[0] = getAccessibleParent();
1539         rRelationSet.AddRelation( ::css::accessibility::AccessibleRelation( ::css::accessibility::AccessibleRelationType::MEMBER_OF, aSequence ) );
1540     }
1541     else
1542     {
1543          VCLXAccessibleComponent::FillAccessibleRelationSet(rRelationSet);
1544     }
1545 }
1546 // virtual
disposing()1547 void SAL_CALL Document::disposing()
1548 {
1549     m_aEngineListener.endListening();
1550     m_aViewListener.endListening();
1551     if (m_xParagraphs.get() != 0)
1552         disposeParagraphs();
1553     VCLXAccessibleComponent::disposing();
1554 }
1555 
1556 // virtual
Notify(::SfxBroadcaster &,::SfxHint const & rHint)1557 void Document::Notify(::SfxBroadcaster &, ::SfxHint const & rHint)
1558 {
1559     if (rHint.ISA(::TextHint))
1560     {
1561         ::TextHint const & rTextHint
1562               = static_cast< ::TextHint const & >(rHint);
1563         switch (rTextHint.GetId())
1564         {
1565         case TEXT_HINT_PARAINSERTED:
1566         case TEXT_HINT_PARAREMOVED:
1567             // TEXT_HINT_PARAINSERTED and TEXT_HINT_PARAREMOVED are sent at
1568             // "unsafe" times (when the text engine has not yet re-formatted its
1569             // content), so that for example calling ::TextEngine::GetTextHeight
1570             // from within the code that handles TEXT_HINT_PARAINSERTED causes
1571             // trouble within the text engine.  Therefore, these hints are just
1572             // buffered until a following ::TextEngine::FormatDoc causes a
1573             // TEXT_HINT_TEXTFORMATTED to come in:
1574         case TEXT_HINT_FORMATPARA:
1575             // ::TextEngine::FormatDoc sends a sequence of
1576             // TEXT_HINT_FORMATPARAs, followed by an optional
1577             // TEXT_HINT_TEXTHEIGHTCHANGED, followed in all cases by one
1578             // TEXT_HINT_TEXTFORMATTED.  Only the TEXT_HINT_FORMATPARAs contain
1579             // the numbers of the affected paragraphs, but they are sent
1580             // before the changes are applied.  Therefore, TEXT_HINT_FORMATPARAs
1581             // are just buffered until another hint comes in:
1582             {
1583                 ::osl::MutexGuard aInternalGuard(GetMutex());
1584                 if (!isAlive())
1585                     break;
1586 
1587                 m_aParagraphNotifications.push(rTextHint);
1588                 break;
1589             }
1590         case TEXT_HINT_TEXTFORMATTED:
1591         case TEXT_HINT_TEXTHEIGHTCHANGED:
1592         case TEXT_HINT_MODIFIED:
1593             {
1594                 ::osl::MutexGuard aInternalGuard(GetMutex());
1595                 if (!isAlive())
1596                     break;
1597                 handleParagraphNotifications();
1598                 break;
1599             }
1600         case TEXT_HINT_VIEWSCROLLED:
1601             {
1602                 ::osl::MutexGuard aInternalGuard(GetMutex());
1603                 if (!isAlive())
1604                     break;
1605                 handleParagraphNotifications();
1606 
1607                 ::sal_Int32 nOffset = static_cast< ::sal_Int32 >(
1608                     m_rView.GetStartDocPos().Y());
1609                     // XXX  numeric overflow
1610                 if (nOffset != m_nViewOffset)
1611                 {
1612                     m_nViewOffset = nOffset;
1613 
1614                     Paragraphs::iterator aOldVisibleBegin(
1615                         m_aVisibleBegin);
1616                     Paragraphs::iterator aOldVisibleEnd(m_aVisibleEnd);
1617 
1618                     determineVisibleRange();
1619 
1620                     notifyVisibleRangeChanges(aOldVisibleBegin,
1621                                                 aOldVisibleEnd,
1622                                                 m_xParagraphs->end());
1623                 }
1624                 break;
1625             }
1626         case TEXT_HINT_VIEWSELECTIONCHANGED:
1627             {
1628                 ::osl::MutexGuard aInternalGuard(GetMutex());
1629                 if (!isAlive())
1630                     break;
1631 
1632                 if (m_aParagraphNotifications.empty())
1633                 {
1634                     handleSelectionChangeNotification();
1635                 }
1636                 else
1637                 {
1638                     // TEXT_HINT_VIEWSELECTIONCHANGED is sometimes sent at
1639                     // "unsafe" times (when the text engine has not yet re-
1640                     // formatted its content), so that for example calling
1641                     // ::TextEngine::GetTextHeight from within the code that
1642                     // handles a previous TEXT_HINT_PARAINSERTED causes
1643                     // trouble within the text engine.  Therefore, these
1644                     // hints are just buffered (along with
1645                     // TEXT_HINT_PARAINSERTED/REMOVED/FORMATPARA) until a
1646                     // following ::TextEngine::FormatDoc causes a
1647                     // TEXT_HINT_TEXTFORMATTED to come in:
1648                     m_bSelectionChangedNotification = true;
1649                 }
1650                 break;
1651             }
1652         }
1653     }
1654 }
1655 
IMPL_LINK(Document,WindowEventHandler,::VclSimpleEvent *,pEvent)1656 IMPL_LINK(Document, WindowEventHandler, ::VclSimpleEvent *, pEvent)
1657 {
1658     switch (pEvent->GetId())
1659     {
1660     case VCLEVENT_WINDOW_RESIZE:
1661         {
1662             ::osl::MutexGuard aInternalGuard(GetMutex());
1663             if (!isAlive())
1664                 break;
1665 
1666             ::sal_Int32 nHeight = static_cast< ::sal_Int32 >(
1667                 m_rView.GetWindow()->GetOutputSizePixel().Height());
1668                 // XXX  numeric overflow
1669             if (nHeight != m_nViewHeight)
1670             {
1671                 m_nViewHeight = nHeight;
1672 
1673                 Paragraphs::iterator aOldVisibleBegin(m_aVisibleBegin);
1674                 Paragraphs::iterator aOldVisibleEnd(m_aVisibleEnd);
1675 
1676                 determineVisibleRange();
1677 
1678                 notifyVisibleRangeChanges(aOldVisibleBegin, aOldVisibleEnd,
1679                                             m_xParagraphs->end());
1680             }
1681             break;
1682         }
1683     case VCLEVENT_WINDOW_GETFOCUS:
1684         {
1685             ::osl::MutexGuard aInternalGuard(GetMutex());
1686             if (!isAlive())
1687                 break;
1688             //to enable the PARAGRAPH to get focus for multiline edit
1689             ::sal_Int32 count = getAccessibleChildCount();
1690             ::sal_Bool bEmpty = m_aFocused == m_aVisibleEnd && count == 1;
1691             if ((m_aFocused >= m_aVisibleBegin && m_aFocused < m_aVisibleEnd) || bEmpty)
1692             {
1693                 Paragraphs::iterator m_aTemp = bEmpty ? m_aVisibleBegin : m_aFocused;
1694                 ::rtl::Reference< ParagraphImpl > xParagraph(getParagraph(m_aTemp));
1695                 if (xParagraph.is())
1696                 {
1697                     xParagraph->notifyEvent(
1698                         ::css::accessibility::AccessibleEventId::
1699                         STATE_CHANGED,
1700                         ::css::uno::Any(),
1701                         ::css::uno::makeAny(
1702                             ::css::accessibility::AccessibleStateType::
1703                             FOCUSED));
1704                 }
1705             }
1706             /*
1707                 ::rtl::Reference< ParagraphImpl > xParagraph(
1708                     getParagraph(m_aFocused));
1709                 if (xParagraph.is())
1710                     xParagraph->notifyEvent(
1711                         ::css::accessibility::AccessibleEventId::
1712                         STATE_CHANGED,
1713                         ::css::uno::Any(),
1714                         ::css::uno::makeAny(
1715                             ::css::accessibility::AccessibleStateType::
1716                             FOCUSED));
1717             */
1718             break;
1719         }
1720     case VCLEVENT_WINDOW_LOSEFOCUS:
1721         {
1722             ::osl::MutexGuard aInternalGuard(GetMutex());
1723             if (!isAlive())
1724                 break;
1725             //to enable the PARAGRAPH to get focus for multiline edit
1726             ::sal_Int32 count = getAccessibleChildCount();
1727             ::sal_Bool bEmpty = m_aFocused == m_aVisibleEnd && count == 1;
1728             if ((m_aFocused >= m_aVisibleBegin && m_aFocused < m_aVisibleEnd) || bEmpty)
1729             {
1730                 Paragraphs::iterator m_aTemp = bEmpty ? m_aVisibleBegin : m_aFocused;
1731                 ::rtl::Reference< ParagraphImpl > xParagraph(getParagraph(m_aTemp));
1732                 if (xParagraph.is())
1733                     xParagraph->notifyEvent(
1734                         ::css::accessibility::AccessibleEventId::
1735                         STATE_CHANGED,
1736                         ::css::uno::makeAny(
1737                             ::css::accessibility::AccessibleStateType::
1738                             FOCUSED),
1739                         ::css::uno::Any());
1740             }
1741 
1742             /*
1743             if (m_aFocused >= m_aVisibleBegin && m_aFocused < m_aVisibleEnd)
1744             {
1745                 ::rtl::Reference< ParagraphImpl > xParagraph(
1746                     getParagraph(m_aFocused));
1747                 if (xParagraph.is())
1748                     xParagraph->notifyEvent(
1749                         ::css::accessibility::AccessibleEventId::
1750                         STATE_CHANGED,
1751                         ::css::uno::makeAny(
1752                             ::css::accessibility::AccessibleStateType::
1753                             FOCUSED),
1754                         ::css::uno::Any());
1755             }
1756             */
1757             break;
1758         }
1759     }
1760     return 0;
1761 }
1762 
init()1763 void Document::init()
1764 {
1765     if (m_xParagraphs.get() == 0)
1766     {
1767         ::sal_uLong nCount = m_rEngine.GetParagraphCount();
1768         ::std::auto_ptr< Paragraphs > p(new Paragraphs);
1769         p->reserve(static_cast< Paragraphs::size_type >(nCount));
1770             // numeric overflow is harmless here
1771         for (::sal_uLong i = 0; i < nCount; ++i)
1772             p->push_back(ParagraphInfo(static_cast< ::sal_Int32 >(
1773                                            m_rEngine.GetTextHeight(i))));
1774                 // XXX  numeric overflow
1775         m_nViewOffset = static_cast< ::sal_Int32 >(
1776             m_rView.GetStartDocPos().Y()); // XXX  numeric overflow
1777         m_nViewHeight = static_cast< ::sal_Int32 >(
1778             m_rView.GetWindow()->GetOutputSizePixel().Height());
1779             // XXX  numeric overflow
1780         m_xParagraphs = p;
1781         determineVisibleRange();
1782         m_nSelectionFirstPara = -1;
1783         m_nSelectionFirstPos = -1;
1784         m_nSelectionLastPara = -1;
1785         m_nSelectionLastPos = -1;
1786         m_aFocused = m_xParagraphs->end();
1787         m_bSelectionChangedNotification = false;
1788         m_aEngineListener.startListening(m_rEngine);
1789         m_aViewListener.startListening(*m_rView.GetWindow());
1790     }
1791 }
1792 
1793 ::rtl::Reference< ParagraphImpl >
getParagraph(Paragraphs::iterator const & rIt)1794 Document::getParagraph(Paragraphs::iterator const & rIt)
1795 {
1796     return static_cast< ParagraphImpl * >(
1797         ::css::uno::Reference< ::css::accessibility::XAccessible >(
1798             rIt->getParagraph()).get());
1799 }
1800 
1801 ::css::uno::Reference< ::css::accessibility::XAccessible >
getAccessibleChild(Paragraphs::iterator const & rIt)1802 Document::getAccessibleChild(Paragraphs::iterator const & rIt)
1803 {
1804     ::css::uno::Reference< ::css::accessibility::XAccessible > xParagraph(
1805         rIt->getParagraph());
1806     if (!xParagraph.is())
1807     {
1808         xParagraph = new Paragraph(this, rIt - m_xParagraphs->begin());
1809         rIt->setParagraph(xParagraph);
1810     }
1811     return xParagraph;
1812 }
1813 
determineVisibleRange()1814 void Document::determineVisibleRange()
1815 {
1816     m_aVisibleBegin = m_xParagraphs->end();
1817     m_aVisibleEnd = m_aVisibleBegin;
1818     ::sal_Int32 nPos = 0;
1819     for (Paragraphs::iterator aIt = m_xParagraphs->begin();;)
1820     {
1821         if (aIt == m_xParagraphs->end())
1822         {
1823             m_nVisibleBeginOffset = 0;
1824             break;
1825         }
1826         ::sal_Int32 nOldPos = nPos;
1827         nPos += aIt->getHeight(); // XXX  numeric overflow
1828         if (m_aVisibleBegin == m_xParagraphs->end() && nPos >= m_nViewOffset)
1829         {
1830             m_aVisibleBegin = aIt;
1831             m_nVisibleBeginOffset = m_nViewOffset - nOldPos;
1832         }
1833         ++aIt;
1834         if (m_aVisibleBegin != m_xParagraphs->end()
1835             && (aIt == m_xParagraphs->end()
1836                 || nPos >= m_nViewOffset + m_nViewHeight))
1837             // XXX  numeric overflow
1838         {
1839             m_aVisibleEnd = aIt;
1840             break;
1841         }
1842     }
1843 }
1844 
notifyVisibleRangeChanges(Paragraphs::iterator const & rOldVisibleBegin,Paragraphs::iterator const & rOldVisibleEnd,Paragraphs::iterator const & rInserted)1845 void Document::notifyVisibleRangeChanges(
1846     Paragraphs::iterator const & rOldVisibleBegin,
1847     Paragraphs::iterator const & rOldVisibleEnd,
1848     Paragraphs::iterator const & rInserted)
1849 {
1850     // XXX  Replace this code that determines which paragraphs have changed from
1851     // invisible to visible or vice versa with a better algorithm.
1852     {for (Paragraphs::iterator aIt(rOldVisibleBegin); aIt != rOldVisibleEnd;
1853           ++aIt)
1854         if (aIt != rInserted
1855             && (aIt < m_aVisibleBegin || aIt >= m_aVisibleEnd))
1856             NotifyAccessibleEvent(
1857                 ::css::accessibility::AccessibleEventId::
1858                 CHILD,
1859                 ::css::uno::makeAny(getAccessibleChild(aIt)),
1860                 ::css::uno::Any());
1861     }
1862     {for (Paragraphs::iterator aIt(m_aVisibleBegin); aIt != m_aVisibleEnd;
1863           ++aIt)
1864         if (aIt == rInserted
1865             || aIt < rOldVisibleBegin || aIt >= rOldVisibleEnd)
1866             NotifyAccessibleEvent(
1867                 ::css::accessibility::AccessibleEventId::
1868                 CHILD,
1869                 ::css::uno::Any(),
1870                 ::css::uno::makeAny(getAccessibleChild(aIt)));
1871     }
1872 }
1873 
1874 void
changeParagraphText(::sal_uLong nNumber,::sal_uInt16 nBegin,::sal_uInt16 nEnd,bool bCut,bool bPaste,::rtl::OUString const & rText)1875 Document::changeParagraphText(::sal_uLong nNumber, ::sal_uInt16 nBegin, ::sal_uInt16 nEnd,
1876                               bool bCut, bool bPaste,
1877                               ::rtl::OUString const & rText)
1878 {
1879     m_rView.SetSelection(::TextSelection(::TextPaM(nNumber, nBegin),
1880                                          ::TextPaM(nNumber, nEnd)));
1881     if (bCut)
1882         m_rView.Cut();
1883     else if (nBegin != nEnd)
1884         m_rView.DeleteSelected();
1885     if (bPaste)
1886         m_rView.Paste();
1887     else if ( !rText.isEmpty() )
1888         m_rView.InsertText(rText);
1889 }
1890 
handleParagraphNotifications()1891 void Document::handleParagraphNotifications()
1892 {
1893     while (!m_aParagraphNotifications.empty())
1894     {
1895         ::TextHint aHint(m_aParagraphNotifications.front());
1896         m_aParagraphNotifications.pop();
1897         switch (aHint.GetId())
1898         {
1899         case TEXT_HINT_PARAINSERTED:
1900             {
1901                 ::sal_uLong n = aHint.GetValue();
1902                 OSL_ENSURE(n <= m_xParagraphs->size(),
1903                            "bad TEXT_HINT_PARAINSERTED event");
1904 
1905                 // Save the values of old iterators (the iterators themselves
1906                 // will get invalidated), and adjust the old values so that they
1907                 // reflect the insertion of the new paragraph:
1908                 Paragraphs::size_type nOldVisibleBegin
1909                     = m_aVisibleBegin - m_xParagraphs->begin();
1910                 Paragraphs::size_type nOldVisibleEnd
1911                     = m_aVisibleEnd - m_xParagraphs->begin();
1912                 Paragraphs::size_type nOldFocused
1913                     = m_aFocused - m_xParagraphs->begin();
1914                 if (n <= nOldVisibleBegin)
1915                     ++nOldVisibleBegin; // XXX  numeric overflow
1916                 if (n <= nOldVisibleEnd)
1917                     ++nOldVisibleEnd; // XXX  numeric overflow
1918                 if (n <= nOldFocused)
1919                     ++nOldFocused; // XXX  numeric overflow
1920                 if (sal::static_int_cast<sal_Int32>(n) <= m_nSelectionFirstPara)
1921                     ++m_nSelectionFirstPara; // XXX  numeric overflow
1922                 if (sal::static_int_cast<sal_Int32>(n) <= m_nSelectionLastPara)
1923                     ++m_nSelectionLastPara; // XXX  numeric overflow
1924 
1925                 Paragraphs::iterator aIns(
1926                     m_xParagraphs->insert(
1927                         m_xParagraphs->begin() + n,
1928                         ParagraphInfo(static_cast< ::sal_Int32 >(
1929                                           m_rEngine.GetTextHeight(n)))));
1930                     // XXX  numeric overflow (2x)
1931 
1932                 determineVisibleRange();
1933                 m_aFocused = m_xParagraphs->begin() + nOldFocused;
1934 
1935                 for (Paragraphs::iterator aIt(aIns);;)
1936                 {
1937                     ++aIt;
1938                     if (aIt == m_xParagraphs->end())
1939                         break;
1940                     ::rtl::Reference< ParagraphImpl > xParagraph(
1941                         getParagraph(aIt));
1942                     if (xParagraph.is())
1943                         xParagraph->numberChanged(true);
1944                 }
1945 
1946                 notifyVisibleRangeChanges(
1947                     m_xParagraphs->begin() + nOldVisibleBegin,
1948                     m_xParagraphs->begin() + nOldVisibleEnd, aIns);
1949                 break;
1950             }
1951         case TEXT_HINT_PARAREMOVED:
1952             {
1953                 ::sal_uLong n = aHint.GetValue();
1954                 if (n == TEXT_PARA_ALL)
1955                 {
1956                     {for (Paragraphs::iterator aIt(m_aVisibleBegin);
1957                           aIt != m_aVisibleEnd; ++aIt)
1958                         NotifyAccessibleEvent(
1959                             ::css::accessibility::AccessibleEventId::
1960                             CHILD,
1961                             ::css::uno::makeAny(getAccessibleChild(aIt)),
1962                             ::css::uno::Any());
1963                     }
1964                     disposeParagraphs();
1965                     m_xParagraphs->clear();
1966                     determineVisibleRange();
1967                     m_nSelectionFirstPara = -1;
1968                     m_nSelectionFirstPos = -1;
1969                     m_nSelectionLastPara = -1;
1970                     m_nSelectionLastPos = -1;
1971                     m_aFocused = m_xParagraphs->end();
1972                 }
1973                 else
1974                 {
1975                     OSL_ENSURE(n < m_xParagraphs->size(),
1976                                "Bad TEXT_HINT_PARAREMOVED event");
1977 
1978                     Paragraphs::iterator aIt(m_xParagraphs->begin() + n);
1979                         // numeric overflow cannot occur
1980 
1981                     // Save the values of old iterators (the iterators
1982                     // themselves will get invalidated), and adjust the old
1983                     // values so that they reflect the removal of the paragraph:
1984                     Paragraphs::size_type nOldVisibleBegin
1985                         = m_aVisibleBegin - m_xParagraphs->begin();
1986                     Paragraphs::size_type nOldVisibleEnd
1987                         = m_aVisibleEnd - m_xParagraphs->begin();
1988                     bool bWasVisible
1989                         = nOldVisibleBegin <= n && n < nOldVisibleEnd;
1990                     Paragraphs::size_type nOldFocused
1991                         = m_aFocused - m_xParagraphs->begin();
1992                     bool bWasFocused = aIt == m_aFocused;
1993                     if (n < nOldVisibleBegin)
1994                         --nOldVisibleBegin;
1995                     if (n < nOldVisibleEnd)
1996                         --nOldVisibleEnd;
1997                     if (n < nOldFocused)
1998                         --nOldFocused;
1999                     if (sal::static_int_cast<sal_Int32>(n) < m_nSelectionFirstPara)
2000                         --m_nSelectionFirstPara;
2001                     else if (sal::static_int_cast<sal_Int32>(n) == m_nSelectionFirstPara)
2002                     {
2003                         if (m_nSelectionFirstPara == m_nSelectionLastPara)
2004                         {
2005                             m_nSelectionFirstPara = -1;
2006                             m_nSelectionFirstPos = -1;
2007                             m_nSelectionLastPara = -1;
2008                             m_nSelectionLastPos = -1;
2009                         }
2010                         else
2011                         {
2012                             ++m_nSelectionFirstPara;
2013                             m_nSelectionFirstPos = 0;
2014                         }
2015                     }
2016                     if (sal::static_int_cast<sal_Int32>(n) < m_nSelectionLastPara)
2017                         --m_nSelectionLastPara;
2018                     else if (sal::static_int_cast<sal_Int32>(n) == m_nSelectionLastPara)
2019                     {
2020                         OSL_ENSURE(m_nSelectionFirstPara < m_nSelectionLastPara,
2021                                    "logic error");
2022                         --m_nSelectionLastPara;
2023                         m_nSelectionLastPos = 0x7FFFFFFF;
2024                     }
2025 
2026                     ::css::uno::Reference< ::css::accessibility::XAccessible >
2027                           xStrong;
2028                     if (bWasVisible)
2029                         xStrong = getAccessibleChild(aIt);
2030                     ::css::uno::WeakReference<
2031                           ::css::accessibility::XAccessible > xWeak(
2032                               aIt->getParagraph());
2033                     aIt = m_xParagraphs->erase(aIt);
2034 
2035                     determineVisibleRange();
2036                     m_aFocused = bWasFocused ? m_xParagraphs->end()
2037                         : m_xParagraphs->begin() + nOldFocused;
2038 
2039                     for (; aIt != m_xParagraphs->end(); ++aIt)
2040                     {
2041                         ::rtl::Reference< ParagraphImpl > xParagraph(
2042                             getParagraph(aIt));
2043                         if (xParagraph.is())
2044                             xParagraph->numberChanged(false);
2045                     }
2046 
2047                     if (bWasVisible)
2048                         NotifyAccessibleEvent(
2049                             ::css::accessibility::AccessibleEventId::
2050                             CHILD,
2051                             ::css::uno::makeAny(getAccessibleChild(aIt)),
2052                             ::css::uno::Any());
2053 
2054                     ::css::uno::Reference< ::css::lang::XComponent > xComponent(
2055                         xWeak.get(), ::css::uno::UNO_QUERY);
2056                     if (xComponent.is())
2057                         xComponent->dispose();
2058 
2059                     notifyVisibleRangeChanges(
2060                         m_xParagraphs->begin() + nOldVisibleBegin,
2061                         m_xParagraphs->begin() + nOldVisibleEnd,
2062                         m_xParagraphs->end());
2063                 }
2064                 break;
2065             }
2066         case TEXT_HINT_FORMATPARA:
2067             {
2068                 ::sal_uLong n = aHint.GetValue();
2069                 OSL_ENSURE(n < m_xParagraphs->size(),
2070                            "Bad TEXT_HINT_FORMATPARA event");
2071 
2072                 (*m_xParagraphs)[static_cast< Paragraphs::size_type >(n)].
2073                     changeHeight(static_cast< ::sal_Int32 >(
2074                                      m_rEngine.GetTextHeight(n)));
2075                     // XXX  numeric overflow
2076                 Paragraphs::iterator aOldVisibleBegin(m_aVisibleBegin);
2077                 Paragraphs::iterator aOldVisibleEnd(m_aVisibleEnd);
2078                 determineVisibleRange();
2079                 notifyVisibleRangeChanges(aOldVisibleBegin, aOldVisibleEnd,
2080                                           m_xParagraphs->end());
2081 
2082                 if (n < m_xParagraphs->size())
2083                 {
2084                     Paragraphs::iterator aIt(m_xParagraphs->begin() + n);
2085                     ::rtl::Reference< ParagraphImpl > xParagraph(getParagraph(aIt));
2086                     if (xParagraph.is())
2087                         xParagraph->textChanged();
2088                 }
2089                 break;
2090             }
2091         default:
2092             OSL_ENSURE(false, "bad buffered hint");
2093             break;
2094         }
2095     }
2096     if (m_bSelectionChangedNotification)
2097     {
2098         m_bSelectionChangedNotification = false;
2099         handleSelectionChangeNotification();
2100     }
2101 }
2102 
getSelectionType(::sal_Int32 nNewFirstPara,::sal_Int32 nNewFirstPos,::sal_Int32 nNewLastPara,::sal_Int32 nNewLastPos)2103 ::sal_Int32 Document::getSelectionType(::sal_Int32 nNewFirstPara, ::sal_Int32 nNewFirstPos, ::sal_Int32 nNewLastPara, ::sal_Int32 nNewLastPos)
2104 {
2105     if (m_nSelectionFirstPara == -1)
2106         return -1;
2107     ::sal_Int32 Osp = m_nSelectionFirstPara, Osl = m_nSelectionFirstPos, Oep = m_nSelectionLastPara, Oel = m_nSelectionLastPos;
2108     ::sal_Int32 Nsp = nNewFirstPara, Nsl = nNewFirstPos, Nep = nNewLastPara, Nel = nNewLastPos;
2109     TextPaM Ns(Nsp, sal_uInt16(Nsl));
2110     TextPaM Ne(Nep, sal_uInt16(Nel));
2111     TextPaM Os(Osp, sal_uInt16(Osl));
2112     TextPaM Oe(Oep, sal_uInt16(Oel));
2113 
2114     if (Os == Oe && Ns == Ne)
2115     {
2116         //only caret moves.
2117         return 1;
2118     }
2119     else if (Os == Oe && Ns != Ne)
2120     {
2121         //old has no selection but new has selection
2122         return 2;
2123     }
2124     else if (Os != Oe && Ns == Ne)
2125     {
2126         //old has selection but new has no selection.
2127         return 3;
2128     }
2129     else if (Os != Oe && Ns != Ne && Osp == Nsp && Osl == Nsl)
2130     {
2131         //both old and new have selections.
2132         if (Oep == Nep )
2133         {
2134             //Send text_selection_change event on Nep
2135 
2136             return 4;
2137         }
2138         else if (Oep < Nep)
2139         {
2140             //all the following examples like 1,2->1,3 means that old start select para is 1, old end select para is 2,
2141             // then press shift up, the new start select para is 1, new end select para is 3;
2142             //for example, 1, 2 -> 1, 3; 4,1 -> 4, 7; 4,1 -> 4, 2; 4,4->4,5
2143             if (Nep >= Nsp)
2144             {
2145                 // 1, 2 -> 1, 3; 4, 1 -> 4, 7; 4,4->4,5;
2146                 if (Oep < Osp)
2147                 {
2148                     // 4,1 -> 4,7;
2149                     return 5;
2150                 }
2151                 else if (Oep >= Osp)
2152                 {
2153                     // 1, 2 -> 1, 3; 4,4->4,5;
2154                     return 6;
2155                 }
2156             }
2157             else
2158             {
2159                 // 4,1 -> 4,2,
2160                 if (Oep < Osp)
2161                 {
2162                     // 4,1 -> 4,2,
2163                     return 7;
2164                 }
2165                 else if (Oep >= Osp)
2166                 {
2167                     // no such condition. Oep > Osp = Nsp > Nep
2168                 }
2169             }
2170         }
2171         else if (Oep > Nep)
2172         {
2173             // 3,2 -> 3,1; 4,7 -> 4,1; 4, 7 -> 4,6; 4,4 -> 4,3
2174             if (Nep >= Nsp)
2175             {
2176                 // 4,7 -> 4,6
2177                 if (Oep <= Osp)
2178                 {
2179                     //no such condition, Oep<Osp=Nsp <= Nep
2180                 }
2181                 else if (Oep > Osp)
2182                 {
2183                     // 4,7 ->4,6
2184                     return 8;
2185                 }
2186             }
2187             else
2188             {
2189                 // 3,2 -> 3,1, 4,7 -> 4,1; 4,4->4,3
2190                 if (Oep <= Osp)
2191                 {
2192                     // 3,2 -> 3,1; 4,4->4,3
2193                     return 9;
2194                 }
2195                 else if (Oep > Osp)
2196                 {
2197                     // 4,7 -> 4,1
2198                     return 10;
2199                 }
2200             }
2201         }
2202     }
2203     return -1;
2204 }
2205 
2206 
sendEvent(::sal_Int32 start,::sal_Int32 end,::sal_Int16 nEventId)2207 void Document::sendEvent(::sal_Int32 start, ::sal_Int32 end, ::sal_Int16 nEventId)
2208 {
2209      Paragraphs::iterator aEnd = ::std::min(m_xParagraphs->begin() + end + 1, m_aVisibleEnd);
2210     for (Paragraphs::iterator aIt = ::std::max(m_xParagraphs->begin() + start, m_aVisibleBegin);
2211          aIt < aEnd; ++aIt)
2212     {
2213         ::rtl::Reference< ParagraphImpl > xParagraph(getParagraph(aIt));
2214         if (xParagraph.is())
2215             xParagraph->notifyEvent(
2216             nEventId,
2217                 ::css::uno::Any(), ::css::uno::Any());
2218     }
2219 }
2220 
handleSelectionChangeNotification()2221 void Document::handleSelectionChangeNotification()
2222 {
2223     ::TextSelection const & rSelection = m_rView.GetSelection();
2224     OSL_ENSURE(rSelection.GetStart().GetPara() < m_xParagraphs->size()
2225                && rSelection.GetEnd().GetPara() < m_xParagraphs->size(),
2226                "bad TEXT_HINT_VIEWSELECTIONCHANGED event");
2227     ::sal_Int32 nNewFirstPara
2228           = static_cast< ::sal_Int32 >(rSelection.GetStart().GetPara());
2229     ::sal_Int32 nNewFirstPos
2230           = static_cast< ::sal_Int32 >(rSelection.GetStart().GetIndex());
2231         // XXX  numeric overflow
2232     ::sal_Int32 nNewLastPara
2233           = static_cast< ::sal_Int32 >(rSelection.GetEnd().GetPara());
2234     ::sal_Int32 nNewLastPos
2235           = static_cast< ::sal_Int32 >(rSelection.GetEnd().GetIndex());
2236         // XXX  numeric overflow
2237 
2238     // Lose focus:
2239     Paragraphs::iterator aIt(m_xParagraphs->begin() + nNewLastPara);
2240     if (m_aFocused != m_xParagraphs->end() && m_aFocused != aIt
2241         && m_aFocused >= m_aVisibleBegin && m_aFocused < m_aVisibleEnd)
2242     {
2243         ::rtl::Reference< ParagraphImpl > xParagraph(getParagraph(m_aFocused));
2244         if (xParagraph.is())
2245             xParagraph->notifyEvent(
2246                 ::css::accessibility::AccessibleEventId::
2247                 STATE_CHANGED,
2248                 ::css::uno::makeAny(
2249                     ::css::accessibility::AccessibleStateType::FOCUSED),
2250                 ::css::uno::Any());
2251     }
2252 
2253     // Gain focus and update cursor position:
2254     if (aIt >= m_aVisibleBegin && aIt < m_aVisibleEnd
2255         && (aIt != m_aFocused
2256             || nNewLastPara != m_nSelectionLastPara
2257             || nNewLastPos != m_nSelectionLastPos))
2258     {
2259         ::rtl::Reference< ParagraphImpl > xParagraph(getParagraph(aIt));
2260         if (xParagraph.is())
2261         {
2262         //disable the first event when user types in empty field.
2263         ::sal_Int32 count = getAccessibleChildCount();
2264         ::sal_Bool bEmpty = count > 1;
2265             //if (aIt != m_aFocused)
2266             if (aIt != m_aFocused && bEmpty)
2267                 xParagraph->notifyEvent(
2268                     ::css::accessibility::AccessibleEventId::
2269                     STATE_CHANGED,
2270                     ::css::uno::Any(),
2271                     ::css::uno::makeAny(
2272                         ::css::accessibility::AccessibleStateType::FOCUSED));
2273             if (nNewLastPara != m_nSelectionLastPara
2274                 || nNewLastPos != m_nSelectionLastPos)
2275                 xParagraph->notifyEvent(
2276                     ::css::accessibility::AccessibleEventId::
2277                     CARET_CHANGED,
2278                     ::css::uno::makeAny< ::sal_Int32 >(
2279                         nNewLastPara == m_nSelectionLastPara
2280                         ? m_nSelectionLastPos : 0),
2281                     ::css::uno::makeAny(nNewLastPos));
2282         }
2283     }
2284     m_aFocused = aIt;
2285 
2286     ::sal_Int32 nMin;
2287     ::sal_Int32 nMax;
2288     ::sal_Int32 ret = getSelectionType(nNewFirstPara, nNewFirstPos, nNewLastPara, nNewLastPos);
2289     switch (ret)
2290     {
2291         case -1:
2292             {
2293                 //no event
2294             }
2295             break;
2296         case 1:
2297             {
2298                 //only caret moved, already handled in above
2299             }
2300             break;
2301         case 2:
2302             {
2303                 //old has no selection but new has selection
2304                 nMin = ::std::min(nNewFirstPara, nNewLastPara);
2305                 nMax = ::std::max(nNewFirstPara, nNewLastPara);
2306                 sendEvent(nMin, nMax,  ::css::accessibility::AccessibleEventId::SELECTION_CHANGED);
2307                 sendEvent(nMin, nMax,  ::css::accessibility::AccessibleEventId::TEXT_SELECTION_CHANGED);
2308             }
2309             break;
2310         case 3:
2311             {
2312                 //old has selection but new has no selection.
2313                 nMin = ::std::min(m_nSelectionFirstPara, m_nSelectionLastPara);
2314                 nMax = ::std::max(m_nSelectionFirstPara, m_nSelectionLastPara);
2315                 sendEvent(nMin, nMax,  ::css::accessibility::AccessibleEventId::SELECTION_CHANGED);
2316                 sendEvent(nMin, nMax,  ::css::accessibility::AccessibleEventId::TEXT_SELECTION_CHANGED);
2317             }
2318             break;
2319         case 4:
2320             {
2321                 //Send text_selection_change event on Nep
2322                 sendEvent(nNewLastPara, nNewLastPara, ::css::accessibility::AccessibleEventId::TEXT_SELECTION_CHANGED);
2323             }
2324             break;
2325         case 5:
2326             {
2327                 // 4, 1 -> 4, 7
2328                 sendEvent(m_nSelectionLastPara, m_nSelectionFirstPara-1, ::css::accessibility::AccessibleEventId::SELECTION_CHANGED);
2329                 sendEvent(nNewFirstPara+1, nNewLastPara, ::css::accessibility::AccessibleEventId::SELECTION_CHANGED);
2330 
2331                 sendEvent(m_nSelectionLastPara, nNewLastPara, ::css::accessibility::AccessibleEventId::TEXT_SELECTION_CHANGED);
2332             }
2333             break;
2334         case 6:
2335             {
2336                 // 1, 2 -> 1, 4; 4,4->4,5;
2337                 sendEvent(m_nSelectionLastPara+1, nNewLastPara, ::css::accessibility::AccessibleEventId::SELECTION_CHANGED);
2338 
2339                 sendEvent(m_nSelectionLastPara, nNewLastPara, ::css::accessibility::AccessibleEventId::TEXT_SELECTION_CHANGED);
2340             }
2341             break;
2342         case 7:
2343             {
2344                 // 4,1 -> 4,3,
2345                 sendEvent(m_nSelectionLastPara +1, nNewLastPara , ::css::accessibility::AccessibleEventId::SELECTION_CHANGED);
2346 
2347                 sendEvent(m_nSelectionLastPara, nNewLastPara, ::css::accessibility::AccessibleEventId::TEXT_SELECTION_CHANGED);
2348             }
2349             break;
2350         case 8:
2351             {
2352                 // 4,7 ->4,5;
2353                 sendEvent(nNewLastPara + 1, m_nSelectionLastPara, ::css::accessibility::AccessibleEventId::SELECTION_CHANGED);
2354 
2355                 sendEvent(nNewLastPara, m_nSelectionLastPara, ::css::accessibility::AccessibleEventId::TEXT_SELECTION_CHANGED);
2356             }
2357             break;
2358         case 9:
2359             {
2360                 // 3,2 -> 3,1; 4,4->4,3
2361                 sendEvent(nNewLastPara, m_nSelectionLastPara - 1, ::css::accessibility::AccessibleEventId::SELECTION_CHANGED);
2362 
2363                 sendEvent(nNewLastPara, m_nSelectionLastPara, ::css::accessibility::AccessibleEventId::TEXT_SELECTION_CHANGED);
2364             }
2365             break;
2366         case 10:
2367             {
2368                 // 4,7 -> 4,1
2369                 sendEvent(m_nSelectionFirstPara + 1, m_nSelectionLastPara, ::css::accessibility::AccessibleEventId::SELECTION_CHANGED);
2370                 sendEvent(nNewLastPara, nNewFirstPara - 1, ::css::accessibility::AccessibleEventId::SELECTION_CHANGED);
2371 
2372                 sendEvent(nNewLastPara, m_nSelectionLastPara, ::css::accessibility::AccessibleEventId::TEXT_SELECTION_CHANGED);
2373             }
2374             break;
2375         default:
2376             break;
2377     }
2378 
2379     /*
2380     // Update both old and new selection.  (Regardless of how the two selections
2381     // look like, there will always be two ranges to the left and right of the
2382     // overlap---the overlap and/or the range to the right of it possibly being
2383     // empty.  Only for these two ranges notifications have to be sent.)
2384 
2385     TextPaM aOldTextStart( static_cast< sal_uLong >( m_nSelectionFirstPara ), static_cast< sal_uInt16 >( m_nSelectionFirstPos ) );
2386     TextPaM aOldTextEnd( static_cast< sal_uLong >( m_nSelectionLastPara ), static_cast< sal_uInt16 >( m_nSelectionLastPos ) );
2387     TextPaM aNewTextStart( static_cast< sal_uLong >( nNewFirstPara ), static_cast< sal_uInt16 >( nNewFirstPos ) );
2388     TextPaM aNewTextEnd( static_cast< sal_uLong >( nNewLastPara ), static_cast< sal_uInt16 >( nNewLastPos ) );
2389 
2390     // justify selections
2391     justifySelection( aOldTextStart, aOldTextEnd );
2392     justifySelection( aNewTextStart, aNewTextEnd );
2393 
2394     sal_Int32 nFirst1;
2395     sal_Int32 nLast1;
2396     sal_Int32 nFirst2;
2397     sal_Int32 nLast2;
2398 
2399     if ( m_nSelectionFirstPara == -1 )
2400     {
2401         // old selection not initialized yet => notify events only for new selection (if not empty)
2402         nFirst1 = aNewTextStart.GetPara();
2403         nLast1 = aNewTextEnd.GetPara() + ( aNewTextStart != aNewTextEnd ? 1 : 0 );
2404         nFirst2 = 0;
2405         nLast2 = 0;
2406     }
2407     else if ( aOldTextStart == aOldTextEnd && aNewTextStart == aNewTextEnd )
2408     {
2409         // old an new selection empty => no events
2410         nFirst1 = 0;
2411         nLast1 = 0;
2412         nFirst2 = 0;
2413         nLast2 = 0;
2414     }
2415     else if ( aOldTextStart != aOldTextEnd && aNewTextStart == aNewTextEnd )
2416     {
2417         // old selection not empty + new selection empty => notify events only for old selection
2418         nFirst1 = aOldTextStart.GetPara();
2419         nLast1 = aOldTextEnd.GetPara() + 1;
2420         nFirst2 = 0;
2421         nLast2 = 0;
2422     }
2423     else if ( aOldTextStart == aOldTextEnd && aNewTextStart != aNewTextEnd )
2424     {
2425         // old selection empty + new selection not empty => notify events only for new selection
2426         nFirst1 = aNewTextStart.GetPara();
2427         nLast1 = aNewTextEnd.GetPara() + 1;
2428         nFirst2 = 0;
2429         nLast2 = 0;
2430     }
2431     else
2432     {
2433         // old and new selection not empty => notify events for the two ranges left and right of the overlap
2434         ::std::vector< TextPaM > aTextPaMs(4);
2435         aTextPaMs[0] = aOldTextStart;
2436         aTextPaMs[1] = aOldTextEnd;
2437         aTextPaMs[2] = aNewTextStart;
2438         aTextPaMs[3] = aNewTextEnd;
2439         ::std::sort( aTextPaMs.begin(), aTextPaMs.end() );
2440 
2441         nFirst1 = aTextPaMs[0].GetPara();
2442         nLast1 = aTextPaMs[1].GetPara() + ( aTextPaMs[0] != aTextPaMs[1] ? 1 : 0 );
2443 
2444         nFirst2 = aTextPaMs[2].GetPara();
2445         nLast2 = aTextPaMs[3].GetPara() + ( aTextPaMs[2] != aTextPaMs[3] ? 1 : 0 );
2446 
2447         // adjust overlapping ranges
2448         if ( nLast1 > nFirst2 )
2449             nLast1 = nFirst2;
2450     }
2451 
2452     // notify selection changes
2453     notifySelectionChange( nFirst1, nLast1 );
2454     notifySelectionChange( nFirst2, nLast2 );
2455     */
2456     m_nSelectionFirstPara = nNewFirstPara;
2457     m_nSelectionFirstPos = nNewFirstPos;
2458     m_nSelectionLastPara = nNewLastPara;
2459     m_nSelectionLastPos = nNewLastPos;
2460 }
2461 
notifySelectionChange(sal_Int32 nFirst,sal_Int32 nLast)2462 void Document::notifySelectionChange( sal_Int32 nFirst, sal_Int32 nLast )
2463 {
2464     if ( nFirst < nLast )
2465     {
2466         Paragraphs::iterator aEnd( ::std::min( m_xParagraphs->begin() + nLast, m_aVisibleEnd ) );
2467         for ( Paragraphs::iterator aIt = ::std::max( m_xParagraphs->begin() + nFirst, m_aVisibleBegin ); aIt < aEnd; ++aIt )
2468         {
2469             ::rtl::Reference< ParagraphImpl > xParagraph( getParagraph( aIt ) );
2470             if ( xParagraph.is() )
2471             {
2472                 xParagraph->notifyEvent(
2473                     ::css::accessibility::AccessibleEventId::SELECTION_CHANGED,
2474                     ::css::uno::Any(), ::css::uno::Any() );
2475                 xParagraph->notifyEvent(
2476                     ::css::accessibility::AccessibleEventId::TEXT_SELECTION_CHANGED,
2477                     ::css::uno::Any(), ::css::uno::Any() );
2478             }
2479         }
2480     }
2481 }
2482 
justifySelection(TextPaM & rTextStart,TextPaM & rTextEnd)2483 void Document::justifySelection( TextPaM& rTextStart, TextPaM& rTextEnd )
2484 {
2485     if ( rTextStart > rTextEnd )
2486     {
2487         TextPaM aTextPaM( rTextStart );
2488         rTextStart = rTextEnd;
2489         rTextEnd = aTextPaM;
2490     }
2491 }
2492 
disposeParagraphs()2493 void Document::disposeParagraphs()
2494 {
2495     for (Paragraphs::iterator aIt(m_xParagraphs->begin());
2496          aIt != m_xParagraphs->end(); ++aIt)
2497     {
2498         ::css::uno::Reference< ::css::lang::XComponent > xComponent(
2499             aIt->getParagraph().get(), ::css::uno::UNO_QUERY);
2500         if (xComponent.is())
2501             xComponent->dispose();
2502     }
2503 }
2504 
2505 // static
mapFontColor(::Color const & rColor)2506 ::css::uno::Any Document::mapFontColor(::Color const & rColor)
2507 {
2508     return ::css::uno::makeAny(
2509         static_cast< ::sal_Int32 >(COLORDATA_RGB(rColor.GetColor())));
2510         // FIXME  keep transparency?
2511 }
2512 
2513 // static
mapFontColor(::css::uno::Any const & rColor)2514 ::Color Document::mapFontColor(::css::uno::Any const & rColor)
2515 {
2516     ::sal_Int32 nColor = 0;
2517     rColor >>= nColor;
2518     return ::Color(static_cast< ::ColorData >(nColor));
2519 }
2520 
2521 // static
mapFontWeight(::FontWeight nWeight)2522 ::css::uno::Any Document::mapFontWeight(::FontWeight nWeight)
2523 {
2524     // Map from ::FontWeight to ::css:awt::FontWeight, depends on order of
2525     // elements in ::FontWeight (vcl/vclenum.hxx):
2526     static float const aWeight[]
2527         = { ::css::awt::FontWeight::DONTKNOW, // WEIGHT_DONTKNOW
2528             ::css::awt::FontWeight::THIN, // WEIGHT_THIN
2529             ::css::awt::FontWeight::ULTRALIGHT, // WEIGHT_ULTRALIGHT
2530             ::css::awt::FontWeight::LIGHT, // WEIGHT_LIGHT
2531             ::css::awt::FontWeight::SEMILIGHT, // WEIGHT_SEMILIGHT
2532             ::css::awt::FontWeight::NORMAL, // WEIGHT_NORMAL
2533             ::css::awt::FontWeight::NORMAL, // WEIGHT_MEDIUM
2534             ::css::awt::FontWeight::SEMIBOLD, // WEIGHT_SEMIBOLD
2535             ::css::awt::FontWeight::BOLD, // WEIGHT_BOLD
2536             ::css::awt::FontWeight::ULTRABOLD, // WEIGHT_ULTRABOLD
2537             ::css::awt::FontWeight::BLACK }; // WEIGHT_BLACK
2538     return ::css::uno::makeAny(aWeight[nWeight]);
2539 }
2540 
2541 // static
mapFontWeight(::css::uno::Any const & rWeight)2542 ::FontWeight Document::mapFontWeight(::css::uno::Any const & rWeight)
2543 {
2544     float nWeight = ::css::awt::FontWeight::NORMAL;
2545     rWeight >>= nWeight;
2546     return nWeight <= ::css::awt::FontWeight::DONTKNOW ? WEIGHT_DONTKNOW
2547         : nWeight <= ::css::awt::FontWeight::THIN ? WEIGHT_THIN
2548         : nWeight <= ::css::awt::FontWeight::ULTRALIGHT ? WEIGHT_ULTRALIGHT
2549         : nWeight <= ::css::awt::FontWeight::LIGHT ? WEIGHT_LIGHT
2550         : nWeight <= ::css::awt::FontWeight::SEMILIGHT ? WEIGHT_SEMILIGHT
2551         : nWeight <= ::css::awt::FontWeight::NORMAL ? WEIGHT_NORMAL
2552         : nWeight <= ::css::awt::FontWeight::SEMIBOLD ? WEIGHT_SEMIBOLD
2553         : nWeight <= ::css::awt::FontWeight::BOLD ? WEIGHT_BOLD
2554         : nWeight <= ::css::awt::FontWeight::ULTRABOLD ? WEIGHT_ULTRABOLD
2555         : WEIGHT_BLACK;
2556 }
2557 
2558 }
2559